Skip to content

Commit

Permalink
feat: allow customizing response in the plugin (#7128)
Browse files Browse the repository at this point in the history
Signed-off-by: spacewander <[email protected]>
  • Loading branch information
spacewander committed Jun 30, 2022
1 parent 888ad6e commit b7d4c52
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 3 deletions.
20 changes: 19 additions & 1 deletion apisix/plugin.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand Down
11 changes: 11 additions & 0 deletions apisix/schema_def.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
}
},
}
}
}

Expand Down
24 changes: 24 additions & 0 deletions docs/en/latest/terminology/plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
24 changes: 24 additions & 0 deletions docs/zh/latest/terminology/plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 的插件是热加载的,不管你是新增、删除还是修改插件,都不需要重启服务。
Expand Down
4 changes: 2 additions & 2 deletions t/admin/plugins.t
Original file line number Diff line number Diff line change
Expand Up @@ -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"\},"s":\{"type":"string"\},"t":\{"minItems":1,"type":"array"\}\},"required":\["i"\],"type":"object"\},"version":0.1\}/
Expand Down Expand Up @@ -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"],"type":"object"},"version":0.1}
{"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_default_delay":{"default":false,"type":"boolean"}},"required":["conn","burst","default_conn_delay","key"],"type":"object"},"version":0.1}
Expand Down
87 changes: 87 additions & 0 deletions t/plugin/plugin.t
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit b7d4c52

Please sign in to comment.