You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by sp...@apache.org on 2022/05/26 02:41:39 UTC

[apisix] branch master updated: feat: allow customizing response in the plugin (#7128)

This is an automated email from the ASF dual-hosted git repository.

spacewander pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git


The following commit(s) were added to refs/heads/master by this push:
     new ef9ba91a6 feat: allow customizing response in the plugin (#7128)
ef9ba91a6 is described below

commit ef9ba91a601d400ef4d43e0dd3378f1d98c3b7d9
Author: 罗泽轩 <sp...@gmail.com>
AuthorDate: Thu May 26 10:41:33 2022 +0800

    feat: allow customizing response in the plugin (#7128)
    
    Signed-off-by: spacewander <sp...@gmail.com>
---
 apisix/plugin.lua                    | 20 ++++++++-
 apisix/schema_def.lua                | 11 +++++
 docs/en/latest/terminology/plugin.md | 24 ++++++++++
 docs/zh/latest/terminology/plugin.md | 24 ++++++++++
 t/admin/plugins.t                    |  4 +-
 t/plugin/plugin.t                    | 87 ++++++++++++++++++++++++++++++++++++
 6 files changed, 167 insertions(+), 3 deletions(-)

diff --git a/apisix/plugin.lua b/apisix/plugin.lua
index da3b848f3..fc76b2546 100644
--- a/apisix/plugin.lua
+++ b/apisix/plugin.lua
@@ -142,6 +142,16 @@ local function load_plugin(name, plugins_list, plugin_type)
         end
 
         properties.disable = plugin_injected_schema.disable
+
+        if properties._meta then
+            core.log.error("invalid plugin [", name,
+                           "]: found forbidden '_meta' field in the schema")
+            return
+        end
+
+        properties._meta = plugin_injected_schema._meta
+        -- new injected fields should be added under `_meta`
+
         plugin.schema['$comment'] = plugin_injected_schema['$comment']
     end
 
@@ -743,11 +753,19 @@ function _M.run_plugin(phase, plugins, api_ctx)
             local phase_func = plugins[i][phase]
             if phase_func then
                 plugin_run = true
-                local code, body = phase_func(plugins[i + 1], api_ctx)
+                local conf = plugins[i + 1]
+                local code, body = phase_func(conf, api_ctx)
                 if code or body then
                     if is_http then
                         if code >= 400 then
                             core.log.warn(plugins[i].name, " exits with http status code ", code)
+
+                            if conf._meta and conf._meta.error_response then
+                                -- Whether or not the original error message is output,
+                                -- always return the configured message
+                                -- so the caller can't guess the real error
+                                body = conf._meta.error_response
+                            end
                         end
 
                         core.response.exit(code, body)
diff --git a/apisix/schema_def.lua b/apisix/schema_def.lua
index 091785d37..767c2fa63 100644
--- a/apisix/schema_def.lua
+++ b/apisix/schema_def.lua
@@ -914,6 +914,17 @@ _M.plugin_injected_schema = {
     ["$comment"] = "this is a mark for our injected plugin schema",
     disable = {
         type = "boolean",
+    },
+    _meta = {
+        type = "object",
+        properties = {
+            error_response = {
+                oneOf = {
+                    { type = "string" },
+                    { type = "object" },
+                }
+            },
+        }
     }
 }
 
diff --git a/docs/en/latest/terminology/plugin.md b/docs/en/latest/terminology/plugin.md
index 7e94bf0b1..4bb12a4e3 100644
--- a/docs/en/latest/terminology/plugin.md
+++ b/docs/en/latest/terminology/plugin.md
@@ -72,6 +72,30 @@ A warning level log as shown below indicates that the request was rejected by th
 ip-restriction exits with http status code 403
 ```
 
+## Plugin Common Configuration
+
+Some common configurations can be applied to the plugin configuration. For example,
+
+```json
+{
+    "jwt-auth": {
+        "_meta": {
+            "error_response": {
+                "message": "Missing credential in request"
+            }
+        }
+    }
+}
+```
+
+the configuration above means customizing the error response from the jwt-auth plugin to '{"message": "Missing credential in request"}'.
+
+### Plugin Common Configuration Under `_meta`
+
+| Name         | Type | Description |
+|--------------|------|-------------|
+| error_response | string/object  | Custom error response |
+
 ## Hot Reload
 
 APISIX Plugins are hot-loaded. This means that there is no need to restart the service if you add, delete, modify plugins, or even if you update the plugin code. To hot-reload, you can send an HTTP request through the [Admin API](../admin-api.md):
diff --git a/docs/zh/latest/terminology/plugin.md b/docs/zh/latest/terminology/plugin.md
index daddd168e..c57767565 100644
--- a/docs/zh/latest/terminology/plugin.md
+++ b/docs/zh/latest/terminology/plugin.md
@@ -66,6 +66,30 @@ local _M = {
 
 如果一个请求因为某个插件而被拒绝,会有类似这样的 warn 日志:`ip-restriction exits with http status code 403`。
 
+## 插件通用配置
+
+一些通用的配置可以应用于插件配置。比如说。
+
+````json
+{
+    "jwt-auth": {
+        "_meta": {
+            "error_response": {
+                "message": "Missing credential in request"
+            }
+        }
+    }
+}
+```
+
+上面的配置意味着将 jwt-auth 插件的错误响应自定义为 '{"message": "Missing credential in request"}'。
+
+### 在 `_meta` 下的插件通用配置
+
+| 名称         | 类型 | 描述           |
+|--------------|------|----------------|
+| error_response | string/object  | 自定义错误响应 |
+
 ## 热加载
 
 APISIX 的插件是热加载的,不管你是新增、删除还是修改插件,都不需要重启服务。
diff --git a/t/admin/plugins.t b/t/admin/plugins.t
index 9581c5389..2bfb5fee3 100644
--- a/t/admin/plugins.t
+++ b/t/admin/plugins.t
@@ -265,7 +265,7 @@ plugins:
         }
     }
 --- response_body eval
-qr/\{"metadata_schema":\{"properties":\{"ikey":\{"minimum":0,"type":"number"\},"skey":\{"type":"string"\}\},"required":\["ikey","skey"\],"type":"object"\},"priority":0,"schema":\{"\$comment":"this is a mark for our injected plugin schema","properties":\{"disable":\{"type":"boolean"\},"i":\{"minimum":0,"type":"number"\},"ip":\{"type":"string"\},"port":\{"type":"integer"\},"s":\{"type":"string"\},"t":\{"minItems":1,"type":"array"\}\},"required":\["i"\],"type":"object"\},"version":0.1\}/
+qr/\{"metadata_schema":\{"properties":\{"ikey":\{"minimum":0,"type":"number"\},"skey":\{"type":"string"\}\},"required":\["ikey","skey"\],"type":"object"\},"priority":0,"schema":\{"\$comment":"this is a mark for our injected plugin schema","properties":\{"_meta":\{"properties":\{"error_response":\{"oneOf":\[\{"type":"string"\},\{"type":"object"\}\]\}\},"type":"object"\},"disable":\{"type":"boolean"\},"i":\{"minimum":0,"type":"number"\},"ip":\{"type":"string"\},"port":\{"type":"integer"\}, [...]
 
 
 
@@ -366,7 +366,7 @@ qr/\{"properties":\{"password":\{"type":"string"\},"username":\{"type":"string"\
         }
     }
 --- response_body
-{"priority":1003,"schema":{"$comment":"this is a mark for our injected plugin schema","properties":{"burst":{"minimum":0,"type":"integer"},"conn":{"exclusiveMinimum":0,"type":"integer"},"default_conn_delay":{"exclusiveMinimum":0,"type":"number"},"disable":{"type":"boolean"},"key":{"type":"string"},"key_type":{"default":"var","enum":["var","var_combination"],"type":"string"},"only_use_default_delay":{"default":false,"type":"boolean"}},"required":["conn","burst","default_conn_delay","key"] [...]
+{"priority":1003,"schema":{"$comment":"this is a mark for our injected plugin schema","properties":{"_meta":{"properties":{"error_response":{"oneOf":[{"type":"string"},{"type":"object"}]}},"type":"object"},"burst":{"minimum":0,"type":"integer"},"conn":{"exclusiveMinimum":0,"type":"integer"},"default_conn_delay":{"exclusiveMinimum":0,"type":"number"},"disable":{"type":"boolean"},"key":{"type":"string"},"key_type":{"default":"var","enum":["var","var_combination"],"type":"string"},"only_use [...]
 
 
 
diff --git a/t/plugin/plugin.t b/t/plugin/plugin.t
index 009a51187..e45f5d5f7 100644
--- a/t/plugin/plugin.t
+++ b/t/plugin/plugin.t
@@ -219,3 +219,90 @@ GET /apisix/plugin/blah
     }
 --- response_body
 ok
+
+
+
+=== TEST 7: plugin with custom error message
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "plugins": {
+                        "jwt-auth": {
+                            "_meta": {
+                                "error_response": {
+                                    "message":"Missing credential in request"
+                                }
+                            }
+                        }
+                    },
+                    "upstream": {
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        },
+                        "type": "roundrobin"
+                    },
+                    "uri": "/hello"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 8: verify, missing token
+--- request
+GET /hello
+--- error_code: 401
+--- response_body
+{"message":"Missing credential in request"}
+
+
+
+=== TEST 9: validate custom error message configuration
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+
+            for _, case in ipairs({
+                {input = true},
+                {input = {
+                    error_response = true
+                }},
+                {input = {
+                    error_response = "OK"
+                }},
+            }) do
+                local code, body = t('/apisix/admin/global_rules/1',
+                    ngx.HTTP_PUT,
+                    {
+                        plugins = {
+                            ["jwt-auth"] = {
+                                _meta = case.input
+                            }
+                        }
+                    }
+                )
+                if code >= 300 then
+                    ngx.print(body)
+                else
+                    ngx.say(body)
+                end
+            end
+        }
+    }
+--- response_body
+{"error_msg":"failed to check the configuration of plugin jwt-auth err: property \"_meta\" validation failed: wrong type: expected object, got boolean"}
+{"error_msg":"failed to check the configuration of plugin jwt-auth err: property \"_meta\" validation failed: property \"error_response\" validation failed: value should match only one schema, but matches none"}
+passed