Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: xml-json-conversion plugin convert xml data from request body to json response, and vice versa #5016

Closed
wants to merge 59 commits into from

Conversation

mangoGoForward
Copy link
Contributor

@mangoGoForward mangoGoForward commented Sep 8, 2021

What this PR does / why we need it:

Summary

name

xml-json-conversion plugin convert xml data from request body to json response, and vice versa

Attributes

Name Type Requirement Default Valid Description
from string optional xml ["xml", "json"] input type
to string optional json ["xml", "json"] output type

how-to-enable

Here's an example, enable this plugin on the specified route:

curl -i http://127.0.0.1:9080/apisix/admin/routes/1  -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
    "uri": "/hello",
    "plugins": {
        "xml-json-conversion": {
            "from": "xml",
            "to": "json"
        }
    },
    "upstream": {
        "type": "roundrobin",
        "nodes": {
            "39.97.63.215:80": 1
        }
    }
}'

test-plugin

Via curl to access

curl -X GET http://127.0.0.1:9080/hello \
-H 'Content-Type: text/xml' \
--data '
<people>
  <person>
    <name>Manoel</name>
    <city>Palmas-TO</city>
  </person>
</people>'


{"people":{"person":{"name":"Manoel","city":"Palmas-TO"}}}

disable-plugin

When you want to disable the xml-json-conversion plugin, it is very simple,
you can delete the corresponding json configuration in the plugin configuration,
no need to restart the service, it will take effect immediately:

curl -i http://127.0.0.1:9080/apisix/admin/routes/1  -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
    "uri": "/hello",
    "upstream": {
        "type": "roundrobin",
        "nodes": {
            "39.97.63.215:80": 1
        }
    }
}'

Pre-submission checklist:

  • Did you explain what problem does this PR solve? Or what new features have been added?
  • Have you added corresponding test cases?
  • Have you modified the corresponding document?
  • Is this PR backward compatible? If it is not backward compatible, please discuss on the mailing list first

xuwei added 11 commits September 1, 2021 13:46
when eureka server return compressed data, use lua-zlib to unzip received response data
xml-json-conversion plugin convert xml data from request body to json response, and vice versa
Docs:
xml-json-conversion.md, include en and zh
TEST:
xml-json-conversion.t
@mangoGoForward
Copy link
Contributor Author

@tzssangglass @tokers Can you help me review this PR? Thanks

@@ -70,6 +70,8 @@ dependencies = {
"ext-plugin-proto = 0.3.0",
"casbin = 1.26.0",
"api7-snowflake = 2.0-1",
"xml2lua = 1.5-2",
"lua-cjson = 2.1.0.6"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lua-cjson has installed? refer to:

local json_encode = require("cjson.safe").encode

cc @spacewander

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. lua-cjson is unneccessary


local _M = {
version = 0.1,
priority = 90,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in config-defalt.yaml, priority is 9, pls keep same.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

Comment on lines 98 to 116
local req_body, err = core.request.get_body()
if err or req_body == nil or req_body == '' then
core.log.error("failed to read request body: ", err)
core.response.exit(400, {error_msg = "invalid request body: " .. err})
end

local from = conf.from
local to = conf.to
if from == to then
return req_body
end

local content_type = core.request.headers()["Content-Type"]
local _f_anon = _switch_anonymous[from]
if _f_anon then
return _f_anon(content_type, req_body, to)
else
return 400, {message = "Operation not supported"}
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is almost the same as the following get_json(), can we extract it into a function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. Thanks

docs/zh/latest/plugins/xml-json-conversion.md Outdated Show resolved Hide resolved
docs/zh/latest/plugins/xml-json-conversion.md Outdated Show resolved Hide resolved
return
end

ngx.say(body)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should check the request body is converted to json format?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.


local function json2xml(table_data)
local xmlStr = xml2lua.toXml(cjson.decode(table_data))
xmlStr = string.gsub(xmlStr, "%s+", "")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Camel-Case naming is not good here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. Thanks

end

local function xml2json(xml_data)
local convertHandler = handler:new()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Camel-Case naming is not good here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.


local _switch_anonymous = {
["json"] = function(content_type, req_body, to)
if "application/json" ~= content_type then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you should use the flowing code to check header is json ,not the equal compare

if string.find(content_type, "application/json", 1, true)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@tzssangglass
Copy link
Member

@mangoGoForward hi, pls resolve the conflicting files

@arthur-zhang
Copy link
Contributor

What are scenes can this plugin be used?

@mangoGoForward
Copy link
Contributor Author

@mangoGoForward hi, pls resolve the conflicting files

done. Already resolved.

@mangoGoForward
Copy link
Contributor Author

Could you help me to review this PR? Or give me some suggestions @tokers @arthur-zhang

return {
{
methods = {"GET"},
uri = "/apisix/plugin/xml-json-conversion",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we remove the API? We don't want to expose more APIs to the public network.


local _switch_anonymous = {
["json"] = function(content_type, req_body, to)
if string.find(content_type, "application/json", 1, true) == nil then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to use core.string.find

default = "json"
}
},
additionalProperties = false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have already removed additionalProperties from other plugins.

local string = require("string")
local parser = require("xml2lua").parser
local table_to_xml = require("xml2lua").toXml
local json_decode = require('cjson.safe').decode
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to use core.json


local function json2xml(table_data)
local xmlStr = table_to_xml(json_decode(table_data))
xmlStr = string.gsub(xmlStr, "%s+", "")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use re.gsub?

local function xml2json(xml_data)
local convert_handler = handler:new()
local parser_handler = parser(convert_handler)
parser_handler:parse(xml_data)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should check if the parse fails.

end

local function json2xml(table_data)
local xmlStr = table_to_xml(json_decode(table_data))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

@@ -347,6 +347,7 @@ plugins: # plugin list (sorted by priority)
#- log-rotate # priority: 100
# <- recommend to use priority (0, 100) for your custom plugins
- example-plugin # priority: 0
- xml-json-conversion # priority: 9
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why "priority: 9" is after "priority: 0"? And we should avoid '(0, 100)' for builtin plugins

GET /t
--- response_body
done
--- no_error_log
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can check no_error_log by default like:

if ((!defined $block->error_log) && (!defined $block->no_error_log)) {

@spacewander
Copy link
Member

I am curious about the use case of this plugin. If it is just converting XML to JSON online, and echoing it back, why should we add it as a feature of API gateway? Maybe we should pass the converted body to the upstream, which could be useful.

@arthur-zhang
Copy link
Contributor

I am curious about the use case of this plugin. If it is just converting XML to JSON online, and echoing it back, why should we add it as a feature of API gateway? Maybe we should pass the converted body to the upstream, which could be useful.

agree

@mangoGoForward
Copy link
Contributor Author

mangoGoForward commented Oct 20, 2021

I am curious about the use case of this plugin. If it is just converting XML to JSON online, and echoing it back, why should we add it as a feature of API gateway? Maybe we should pass the converted body to the upstream, which could be useful.

I think so right now, thanks for your suggestion. @spacewander @arthur-zhang

@spacewander
Copy link
Member

We can introduce an "echo_back" option (default to false), so the current behavior can be persisted when echo_back is true.

@mangoGoForward
Copy link
Contributor Author

We can introduce an "echo_back" option (default to false), so the current behavior can be persisted when echo_back is true.

I‘m a little bit incomprehensible, could you please provide more detailed description. And It seems that the plugin is unsuitable as a apisix plugin.

@spacewander
Copy link
Member

"xml-json-conversion": {
            "from": "xml",
            "to": "json",
			"echo_back": true
        }

will give us the current behavior.

"xml-json-conversion": {
            "from": "xml",
            "to": "json"
        }

will pass the rewritten body to the upstream.

@mangoGoForward
Copy link
Contributor Author

"xml-json-conversion": {
            "from": "xml",
            "to": "json",
			"echo_back": true
        }

will give us the current behavior.

"xml-json-conversion": {
            "from": "xml",
            "to": "json"
        }

will pass the rewritten body to the upstream.

I think echo_back is unneccessary, we can registry a route or consumer with this plugin if need to process, and if not need, why we should use this plugin. I'm confused that the plugin's scenes mainly, maybe close this PR is better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants