body-transformer
body-transformer 插件执行基于模板的转换,将请求和/或响应体从一种格式转换为另一种格式。
示例
以下示例演示了如何在不同场景下配置 body-transformer。
转换模板使用 lua-resty-template 语法。有关详细信息,请参阅模板语法。
你还可以使用辅助函数 _escape_json() 和 _escape_xml() 来转义双引号等特殊字符,使用 _body 访问请求体,使用 _ctx 访问上下文变量。
在所有情况下,你应确保转换模板是一个有效的 JSON 字符串。
在 JSON 和 XML SOAP 之间转换
以下示例演示了如何在与 SOAP 上游服务一起工作时,将请求体从 JSON 转换为 XML,并将响应体从 XML 转换为 JSON。
启动示例 SOAP 服务:
cd /tmp
git clone https://github.com/spring-guides/gs-soap-service.git
cd gs-soap-service/complete
./mvnw spring-boot:run
创建请求和响应转换模板:
req_template=$(cat <<EOF | awk '{gsub(/"/,"\\\"");};1' | awk '{$1=$1};1' | tr -d '\r\n'
<?xml version="1.0"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Body>
<ns0:getCountryRequest xmlns:ns0="http://spring.io/guides/gs-producing-web-service">
<ns0:name>{{_escape_xml(name)}}</ns0:name>
</ns0:getCountryRequest>
</soap-env:Body>
</soap-env:Envelope>
EOF
)
rsp_template=$(cat <<EOF | awk '{gsub(/"/,"\\\"");};1' | awk '{$1=$1};1' | tr -d '\r\n'
{% if Envelope.Body.Fault == nil then %}
{
"status":"{{_ctx.var.status}}",
"currency":"{{Envelope.Body.getCountryResponse.country.currency}}",
"population":{{Envelope.Body.getCountryResponse.country.population}},
"capital":"{{Envelope.Body.getCountryResponse.country.capital}}",
"name":"{{Envelope.Body.getCountryResponse.country.name}}"
}
{% else %}
{
"message":{*_escape_json(Envelope.Body.Fault.faultstring[1])*},
"code":"{{Envelope.Body.Fault.faultcode}}"
{% if Envelope.Body.Fault.faultactor ~= nil then %}
, "actor":"{{Envelope.Body.Fault.faultactor}}"
{% end %}
}
{% end %}
EOF
)
上面使用了 awk 和 tr 来操作模板,以确保模板是一个有效的 JSON 字符串。
使用之前创建的模板创建一个带有 body-transformer 的路由:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "body-transformer-route",
"methods": ["POST"],
"uri": "/ws",
"plugins": {
"body-transformer": {
"request": {
"template": "'"$req_template"'",
// Annotate 1
"input_format": "json"
},
"response": {
"template": "'"$rsp_template"'",
// Annotate 2
"input_format": "xml"
}
},
"proxy-rewrite": {
"headers": {
"set": {
// Annotate 3
"Content-Type": "text/xml"
}
}
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"localhost:8080": 1
}
}
}'
❶ 将请求输入格式设置为 JSON,以便插件在内部应用 JSON 解码器。
❷ 将响应输入格式设置为 XML,以便插件在内部应用 XML 解码器。
❸ 将 Content-Type 头设置为 text/xml,以便上游服务正确响应。
如果调整复杂的文本文件使其成为有效的转换模板比较麻烦,你可以使用 base64 工具对文件进行编码,如下所示:
"body-transformer": {
"request": {
"template": "'"$(base64 -w0 /path/to/request_template_file)"'"
},
"response": {
"template": "'"$(base64 -w0 /path/to/response_template_file)"'"
}
}
发送带有有效 JSON 体的请求:
curl "http://127.0.0.1:9080/ws" -X POST -d '{"name": "Spain"}'
请求中发送的 JSON 体将在转发到上游 SOAP 服务之前转换为 XML,响应体将从 XML 转换回 JSON。
你应该看到类似于以下的响应:
{
"status": "200",
"currency": "EUR",
"population": 46704314,
"capital": "Madrid",
"name": "Spain"
}
修改请求体
以下示例演示了如何动态修改请求体。
创建一个带有 body-transformer 的路由:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "body-transformer-route",
"uri": "/anything",
"plugins": {
"body-transformer": {
"request": {
// Annotate 1
"template": "{\"foo\":\"{{name .. \" world\"}}\",\"bar\":{{age+10}}}"
}
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
❶ 设置一个模板,将 "world" 追加到 name,并将 10 加到 age,然后将它们分别设置为 "foo" 和 "bar" 的值。
发送请求到路由:
curl "http://127.0.0.1:9080/anything" -X POST \
-H "Content-Type: application/json" \
-d '{"name":"hello","age":20}' \
-i
你应该看到以下响应:
{
"args": {},
"data": "{\"foo\":\"hello world\",\"bar\":30}",
...
"json": {
"bar": 30,
"foo": "hello world"
},
"method": "POST",
...
}
使用变量生成请求体
以下示例演示了如何使用 ctx 上下文变量动态生成请求体。
创建一个带有 body-transformer 的路由:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "body-transformer-route",
"uri": "/anything",
"plugins": {
"body-transformer": {
"request": {
// Annotate 1
"template": "{\"foo\":\"{{_ctx.var.arg_name .. \" world\"}}\"}"
}
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
❶ 设置一个模板,使用 NGINX 变量 arg_name 访问请求参数。
发送带有 name 参数的请求到路由:
curl -i "http://127.0.0.1:9080/anything?name=hello"
你应该看到像这样的响应:
{
"args": {
"name": "hello"
},
...,
"json": {
"foo": "hello world"
},
...
}