mirror of
https://gitee.com/iresty/apisix.git
synced 2024-12-05 21:47:36 +08:00
feat: add rejected_message support for plugin request-validation (#5122)
This commit is contained in:
parent
c3635d46ef
commit
76540a57f5
@ -17,27 +17,20 @@
|
||||
local core = require("apisix.core")
|
||||
local plugin_name = "request-validation"
|
||||
local ngx = ngx
|
||||
local io = io
|
||||
local io = io
|
||||
local req_read_body = ngx.req.read_body
|
||||
local req_get_body_data = ngx.req.get_body_data
|
||||
|
||||
local schema = {
|
||||
type = "object",
|
||||
properties = {
|
||||
header_schema = {type = "object"},
|
||||
body_schema = {type = "object"},
|
||||
rejected_msg = {type = "string", minLength = 1, maxLength = 256}
|
||||
},
|
||||
anyOf = {
|
||||
{
|
||||
title = "Body schema",
|
||||
properties = {
|
||||
body_schema = {type = "object"}
|
||||
},
|
||||
required = {"body_schema"}
|
||||
},
|
||||
{
|
||||
title = "Header schema",
|
||||
properties = {
|
||||
header_schema = {type = "object"}
|
||||
},
|
||||
required = {"header_schema"}
|
||||
}
|
||||
{required = {"header_schema"}},
|
||||
{required = {"body_schema"}}
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,7 +75,7 @@ function _M.rewrite(conf)
|
||||
local ok, err = core.schema.check(conf.header_schema, headers)
|
||||
if not ok then
|
||||
core.log.error("req schema validation failed", err)
|
||||
return 400, err
|
||||
return 400, conf.rejected_msg or err
|
||||
end
|
||||
end
|
||||
|
||||
@ -94,11 +87,11 @@ function _M.rewrite(conf)
|
||||
if not body then
|
||||
local filename = ngx.req.get_body_file()
|
||||
if not filename then
|
||||
return 500
|
||||
return 500, conf.rejected_msg
|
||||
end
|
||||
local fd = io.open(filename, 'rb')
|
||||
if not fd then
|
||||
return 500
|
||||
return 500, conf.rejected_msg
|
||||
end
|
||||
body = fd:read('*a')
|
||||
end
|
||||
@ -111,13 +104,13 @@ function _M.rewrite(conf)
|
||||
|
||||
if not req_body then
|
||||
core.log.error('failed to decode the req body', error)
|
||||
return 400, error
|
||||
return 400, conf.rejected_msg or error
|
||||
end
|
||||
|
||||
local ok, err = core.schema.check(conf.body_schema, req_body)
|
||||
if not ok then
|
||||
core.log.error("req schema validation failed", err)
|
||||
return 400, err
|
||||
return 400, conf.rejected_msg or err
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -39,10 +39,13 @@ For more information on schema, refer to [JSON schema](https://github.com/api7/j
|
||||
|
||||
## Attributes
|
||||
|
||||
| Name | Type | Requirement | Default | Valid | Description |
|
||||
| ------------- | ------ | ----------- | ------- | ----- | -------------------------- |
|
||||
| header_schema | object | optional | | | schema for the header data |
|
||||
| body_schema | object | optional | | | schema for the body data |
|
||||
> Note that at least one of `header_schema` and `body_schema` must be filled in.
|
||||
|
||||
| Name | Type | Requirement | Default | Valid | Description |
|
||||
| ---------------- | ------ | ----------- | ------- | ----- | -------------------------- |
|
||||
| header_schema | object | optional | | | schema for the header data |
|
||||
| body_schema | object | optional | | | schema for the body data |
|
||||
| rejected_message | string | optional | | | the custom rejected message |
|
||||
|
||||
## How To Enable
|
||||
|
||||
@ -60,7 +63,8 @@ curl http://127.0.0.1:9080/apisix/admin/routes/5 -H 'X-API-KEY: edd1c9f034335f13
|
||||
"properties": {
|
||||
"required_payload": {"type": "string"},
|
||||
"boolean_payload": {"type": "boolean"}
|
||||
}
|
||||
},
|
||||
"rejected_message": "customize reject message"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -82,7 +86,7 @@ curl --header "Content-Type: application/json" \
|
||||
http://127.0.0.1:9080/get
|
||||
```
|
||||
|
||||
If the schema is violated the plugin will yield a `400` bad request.
|
||||
If the schema is violated the plugin will yield a `400` bad request with the reject response.
|
||||
|
||||
## Disable Plugin
|
||||
|
||||
@ -252,3 +256,30 @@ curl http://127.0.0.1:9080/apisix/admin/routes/5 -H 'X-API-KEY: edd1c9f034335f13
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Custom rejected message:**
|
||||
|
||||
```json
|
||||
{
|
||||
"uri": "/get",
|
||||
"plugins": {
|
||||
"request-validation": {
|
||||
"body_schema": {
|
||||
"type": "object",
|
||||
"required": ["required_payload"],
|
||||
"properties": {
|
||||
"required_payload": {"type": "string"},
|
||||
"boolean_payload": {"type": "boolean"}
|
||||
},
|
||||
"rejected_message": "customize reject message"
|
||||
}
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"type": "roundrobin",
|
||||
"nodes": {
|
||||
"127.0.0.1:8080": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -38,10 +38,13 @@ title: request-validation
|
||||
|
||||
## 属性
|
||||
|
||||
| Name | Type | Requirement | Default | Valid | Description |
|
||||
| ------------- | ------ | ----------- | ------- | ----- | --------------------------------- |
|
||||
| header_schema | object | 可选 | | | `header` 数据的 `schema` 数据结构 |
|
||||
| body_schema | object | 可选 | | | `body` 数据的 `schema` 数据结构 |
|
||||
> 注意, `header_schema` 与 `body_schema` 至少填写其中一个
|
||||
|
||||
| Name | Type | Requirement | Default | Valid | Description |
|
||||
| ---------------- | ------ | ----------- | ------- | ----- | --------------------------------- |
|
||||
| header_schema | object | 可选 | | | `header` 数据的 `schema` 数据结构 |
|
||||
| body_schema | object | 可选 | | | `body` 数据的 `schema` 数据结构 |
|
||||
| rejected_message | string | 可选 | | | 自定义拒绝信息 |
|
||||
|
||||
## 如何启用
|
||||
|
||||
@ -59,7 +62,8 @@ curl http://127.0.0.1:9080/apisix/admin/routes/5 -H 'X-API-KEY: edd1c9f034335f13
|
||||
"properties": {
|
||||
"required_payload": {"type": "string"},
|
||||
"boolean_payload": {"type": "boolean"}
|
||||
}
|
||||
},
|
||||
"rejected_message": "customize reject message"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -81,7 +85,7 @@ curl --header "Content-Type: application/json" \
|
||||
http://127.0.0.1:9080/get
|
||||
```
|
||||
|
||||
如果 `Schema` 验证失败,将返回 `400 bad request` 错误。
|
||||
如果 `Schema` 验证失败,将返回 `400` 状态码与相应的拒绝信息。
|
||||
|
||||
## 禁用插件
|
||||
|
||||
@ -250,3 +254,30 @@ curl http://127.0.0.1:9080/apisix/admin/routes/5 -H 'X-API-KEY: edd1c9f034335f13
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**自定义拒绝信息:**
|
||||
|
||||
```json
|
||||
{
|
||||
"uri": "/get",
|
||||
"plugins": {
|
||||
"request-validation": {
|
||||
"body_schema": {
|
||||
"type": "object",
|
||||
"required": ["required_payload"],
|
||||
"properties": {
|
||||
"required_payload": {"type": "string"},
|
||||
"boolean_payload": {"type": "boolean"}
|
||||
},
|
||||
"rejected_message": "customize reject message"
|
||||
}
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"type": "roundrobin",
|
||||
"nodes": {
|
||||
"127.0.0.1:8080": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
295
t/plugin/request-validation.t
vendored
295
t/plugin/request-validation.t
vendored
@ -61,9 +61,8 @@ done
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
object matches none of the requireds: ["body_schema"] or ["header_schema"]
|
||||
done
|
||||
--- response_body_like eval
|
||||
qr/object matches none of the requireds/
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -375,8 +374,6 @@ hello1 world
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -415,8 +412,6 @@ passed
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -455,8 +450,6 @@ passed
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -495,8 +488,6 @@ passed
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -535,8 +526,6 @@ passed
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -575,8 +564,6 @@ passed
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -615,8 +602,6 @@ passed
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -655,8 +640,6 @@ passed
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -787,8 +770,6 @@ qr/table expected, got string/
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -881,8 +862,6 @@ qr/table expected, got string/
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -939,8 +918,6 @@ passed
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -979,8 +956,6 @@ passed
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -1019,8 +994,6 @@ passed
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -1059,8 +1032,6 @@ passed
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -1099,8 +1070,6 @@ passed
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -1139,8 +1108,6 @@ passed
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -1179,8 +1146,6 @@ passed
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -1219,8 +1184,6 @@ passed
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -1351,8 +1314,6 @@ qr/table expected, got string/
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -1445,7 +1406,255 @@ qr/table expected, got string/
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- error_code chomp
|
||||
200
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 35: add route (test request validation `header_schema.required` success with custom reject 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": {
|
||||
"request-validation": {
|
||||
"header_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"test": {
|
||||
"type": "string",
|
||||
"enum": ["a", "b", "c"]
|
||||
}
|
||||
},
|
||||
"required": ["test"]
|
||||
},
|
||||
"rejected_msg": "customize reject message for header_schema.required"
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:1982": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
"uri": "/opentracing"
|
||||
}]])
|
||||
if code >= 300 then
|
||||
ngx.status = code
|
||||
end
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 36: use empty header to hit `header_schema.required with custom reject message` rule
|
||||
--- request
|
||||
GET /opentracing
|
||||
--- error_code: 400
|
||||
--- response_body chomp
|
||||
customize reject message for header_schema.required
|
||||
--- error_log eval
|
||||
qr/schema validation failed/
|
||||
|
||||
|
||||
|
||||
=== TEST 37: use bad header value to hit `header_schema.required with custom reject message` rule
|
||||
--- request
|
||||
GET /opentracing
|
||||
--- more_headers
|
||||
test: abc
|
||||
--- error_code: 400
|
||||
--- response_body chomp
|
||||
customize reject message for header_schema.required
|
||||
--- error_log eval
|
||||
qr/schema validation failed/
|
||||
|
||||
|
||||
|
||||
=== TEST 38: pass `header_schema.required with custom reject message` rule
|
||||
--- request
|
||||
GET /opentracing
|
||||
--- more_headers
|
||||
test: a
|
||||
--- error_code: 200
|
||||
--- response_body eval
|
||||
qr/opentracing/
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 39: add route (test request validation `body_schema.required` success with custom reject 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": {
|
||||
"request-validation": {
|
||||
"body_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"test": {
|
||||
"type": "string",
|
||||
"enum": ["a", "b", "c"]
|
||||
}
|
||||
},
|
||||
"required": ["test"]
|
||||
},
|
||||
"rejected_msg": "customize reject message for body_schema.required"
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:1982": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
"uri": "/opentracing"
|
||||
}]])
|
||||
if code >= 300 then
|
||||
ngx.status = code
|
||||
end
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 40: use empty body to hit `body_schema.required with custom reject message` rule
|
||||
--- request
|
||||
GET /opentracing
|
||||
--- error_code: 500
|
||||
--- response_body chomp
|
||||
customize reject message for body_schema.required
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 41: use bad body value to hit `body_schema.required with custom reject message` rule
|
||||
--- request
|
||||
POST /opentracing
|
||||
{"test":"abc"}
|
||||
--- error_code: 400
|
||||
--- response_body chomp
|
||||
customize reject message for body_schema.required
|
||||
--- error_log eval
|
||||
qr/schema validation failed/
|
||||
|
||||
|
||||
|
||||
=== TEST 42: pass `body_schema.required with custom reject message` rule
|
||||
--- request
|
||||
POST /opentracing
|
||||
{"test":"a"}
|
||||
--- error_code: 200
|
||||
--- response_body eval
|
||||
qr/opentracing/
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 43: add route (test request validation `header_schema.required` failure with custom reject 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": {
|
||||
"request-validation": {
|
||||
"header_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"test": {
|
||||
"type": "string",
|
||||
"enum": ["a", "b", "c"]
|
||||
}
|
||||
},
|
||||
"required": ["test"]
|
||||
},
|
||||
"rejected_msg": "customize reject message customize reject message customize reject message customize reject message customize reject message customize reject message customize reject message customize reject message customize reject message customize reject message customize reject message"
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:1982": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
"uri": "/plugin/request/validation"
|
||||
}]])
|
||||
if code >= 300 then
|
||||
ngx.status = code
|
||||
end
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body_like eval
|
||||
qr/string too long/
|
||||
--- error_code chomp
|
||||
400
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 44: add route (test request validation schema with custom reject message only)
|
||||
--- 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": {
|
||||
"request-validation": {
|
||||
"rejected_message": "customize reject message"
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:1982": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
"uri": "/plugin/request/validation"
|
||||
}]])
|
||||
if code >= 300 then
|
||||
ngx.status = code
|
||||
end
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body_like eval
|
||||
qr/object matches none of the requireds/
|
||||
--- error_code chomp
|
||||
400
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
Loading…
Reference in New Issue
Block a user