Парсинг и модификация

Как по шаблону преобразовать тело запроса перед отправкой на upstream и тело ответа перед возвратом клиенту.

Эта статья — про изменение body по шаблону: например, разобрать XML и собрать из его полей JSON.

Если вам нужно преобразовать response body с помощью регулярных выражений или просто заменить его полностью, используйте плагин response-rewrite по инструкции Работа с payload.

Что это

Парсинг и модификация контента — обработка тела запроса или ответа на уровне Application Delivery Controller.

Система работает с payload: разбирает исходное body, извлекает из него данные и собирает новое body по заданному шаблону.

Как работает

  1. Клиент отправляет запрос.

  2. Наша система получает запрос и сопоставляет его с Route, в котором настроен плагин body-transformer.

  3. Система определяет, как получить входные данные для шаблона.

    Формат body можно указать явно в input_format, либо система попытается определить его по заголовку Content-Type. Чтобы использовать query-параметры GET-запроса как входные данные для шаблона, укажите input_format: args. Также query-параметры можно получать через переменные контекста, например _ctx.var.arg_name.

  4. Система подготавливает входные данные в исходном формате.

    Для запроса поддерживаются body-форматы xml, json, encoded, plain и multipart, а также query-параметры через args . Для ответа — xml и json.

  5. Подготовленные данные становятся доступны в шаблоне, который вы настроили.

    Поля из разобранного JSON, XML, encoded-body или query-параметров можно использовать напрямую. Для multipart-запросов данные формы доступны через context, например context.age, а multipart-body можно изменять через объект context._multipart. Также в шаблоне доступны исходное тело через _body, переменные контекста через _ctx, функции _escape_json() и _escape_xml().

  6. Система применяет шаблон и собирает новое body.

    Если преобразуется запрос, новое body отправляется на upstream. Если преобразуется ответ, новое body возвращается клиенту.

Когда пригодится

Предположим, у вас есть новый публичный REST API, но внутри всё ещё работает старый SOAP-сервис. Клиенты отправляют JSON с понятной структурой, а бэкенд ждёт XML/SOAP.

Чтобы связать эти части системы, вы добавляете плагин body-transformer и настраиваете преобразование JSON в XML перед отправкой запроса на upstream. Если нужно, можно настроить и обратное преобразование: из XML-ответа бэкенда в JSON-ответ для клиента.

В результате клиент работает с обычным JSON API, а старый сервис получает запросы в нужном ему формате.

Если upstream ожидает не только другой формат body, но и другой Content-Type, заголовок нужно настроить отдельно.

Какой плагин используется

body-transformer

Атрибуты плагина

Атрибут Тип Обязательный Значение по умолчанию Допустимые значения Что задаёт

request

object

Нет

Настройки преобразования тела запроса.

request.input_
format

string

Нет

Определяется по Content-Type

xml, json, encoded, args, plain, multipart

Исходный формат данных для шаблона: body запроса или query-параметры GET-запроса при значении args. Для multipart-запросов при необходимости задайте input_format: multipart явно.

request.template

string

Да, если задан request

Шаблон для нового тела запроса.

request.template_
is_base64

boolean

Нет

false

true, false

Показывает, что шаблон запроса передан в формате base64.

response

object

Нет

Настройки преобразования тела ответа.

response.input_
format

string

Нет

Определяется по Content-Type

xml, json

Исходный формат тела ответа.

response.template

string

Да, если задан response

Шаблон для нового тела ответа.

response.template_
is_base64

boolean

Нет

false

true, false

Показывает, что шаблон ответа передан в формате base64.

Что важно учесть

Если input_format не задан, Application Delivery Controller пытается определить формат body по Content-Type. Для query-параметров GET-запроса формат нужно указать явно: input_format: args — этот режим использует query-параметры как входные данные для шаблона, но не меняет HTTP-метод запроса. Если формат не распознан, шаблон применяется напрямую.

Шаблоны используют синтаксис lua-resty-template. Значение template должно быть валидной JSON-строкой. Сам результат преобразования не обязан быть JSON: шаблон может сформировать JSON, XML, HTML, YAML или другой текстовый формат.

Если шаблон содержит кавычки, переносы строк или большой XML-фрагмент, его нужно корректно экранировать или передать в base64 и включить template_is_base64.

Для plain тело запроса не разбирается на поля. Исходная строка доступна в шаблоне через _body.

Как подключить плагин

Через API

Плагин можно добавить к Route через Admin API. Для этого отправьте запрос на создание или обновление Route:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
  -H "X-API-KEY: ${admin_key}" \
  -d '{
    "id": "route-id",
    "uri": "/example",
    "plugins": {
      "body-transformer": {
        "...": "ваши настройки плагина"
      }
    },
    "upstream": {
      "...": "ваши настройки upstream"
    }
  }'

Здесь id — идентификатор Route, uri — путь, по которому запрос попадает в Route, plugins.body-transformer содержит конфигурацию плагина, а upstream — настройки бэкенд-серверов.

Через UI

  1. Откройте раздел Маршруты и нажмите + Добавить.

  2. В настройках маршрута (Route) перейдите в раздел Плагины.

  3. Нажмите + Добавить и выберите body-transformer.

  4. Укажите конфигурацию плагина как JSON-объект.

  5. Сохраните созданный маршрут (Route). Он будет добавлен с подключённым плагином.

Пример конфигурации плагина

В этом примере система принимает JSON, меняет структуру body и отправляет в upstream уже преобразованное тело.

Исходный запрос:

{
  "product_id": "SKU-1001",
  "quantity": 2,
  "source_channel": "mobile"
}

Настройка плагина body-transformer :

{
  "plugins": {
    "body-transformer": {
      "request": {
        "input_format": "json",
        "template": "{\"item\":\"{{product_id}}\",\"count\":{{quantity}},\"channel\":\"{{source_channel}}\",\"source\":\"api-gateway\"}"
      }
    }
  }
}

После преобразования upstream получит такое тело запроса:

{
  "item": "SKU-1001",
  "count": 2,
  "channel": "mobile",
  "source": "api-gateway"
}

Здесь input_format: json говорит системе разобрать входной body как JSON. В template задана новая структура тела: product_id превращается в item, quantity — в count, source_channel — в channel, а поле source добавляется на уровне нашей системы.