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: allow customizing response in the plugin #7128

Merged
merged 1 commit into from
May 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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