跳到主要内容

oas-validator

oas-validator 插件可根据符合 OpenAPI v3 的既定 API 规范验证 HTTP 请求和响应。从 API7 企业版 3.9.8 起支持 OpenAPI 3.1,包括数值形式的 exclusiveMinimum/exclusiveMaximumif/then/else 条件 Schema、通过 ["string", "null"] 定义的可空类型,以及 constpatternPropertiesprefixItems 和 JSON Schema $dynamicRef/$dynamicAnchor 等功能。

示例

继续之前,请获取 Swagger Petstore 的 OpenAPI 规范,后续示例将使用该规范。

export OPEN_API_SPEC=$(curl -s "https://petstore3.swagger.io/api/v3/openapi.json" | sed 's/"/\\"/g')

验证请求体

此示例演示了如何根据给定规范验证请求体。

创建一个使用 OAS validator 插件的路由:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "body_validation",
"uri": "/*",
"plugins": {
"oas-validator": {
"spec": "'"${OPEN_API_SPEC}"'"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"petstore3.swagger.io:443": 1
},
"scheme": "https",
"pass_host": "node"
}
}'

验证失败

使用不满足定义的 Open API 规范的请求体向上述路由发送请求:

curl -i "http://127.0.0.1:9080/api/v3/pet" -X POST \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"invalid-body": "this is an invalid body"}'

你应该会看到 HTTP/1.1 400 Bad Request 响应,其响应体类似于以下内容:

{"message":"failed to validate request."}

验证成功

使用有效的请求体向路由发送请求:

curl -i "http://127.0.0.1:9080/api/v3/pet" -X POST \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"id": 1,
"name": "doggie",
"category": { "id": 1, "name": "Dogs" },
"photoUrls": ["string"],
"tags": [{ "id": 1, "name": "tag1" }],
"status": "available"
}'

你应该会看到 HTTP/1.1 200 OK 响应,其响应体类似于以下内容:

{
"id": 1,
"category": { "id": 1, "name": "Dogs" },
"name": "doggie",
"photoUrls": ["string"],
"tags": [{ "id": 1, "name": "tag1" }],
"status": "available"
}

获取详细的错误响应

此示例演示了如何在验证失败时获取详细的错误响应。

创建一个从 URL 获取规范的路由:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "body_validation",
"uri": "/*",
"plugins": {
"oas-validator": {
"spec": "'"${OPEN_API_SPEC}"'",
"verbose_errors": true
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"petstore3.swagger.io:443": 1
},
"scheme": "https",
"pass_host": "node"
}
}'

使用无效的请求体向上面创建的路由发送请求:

curl -i "http://127.0.0.1:9080/api/v3/pet" -X POST \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"invalid-body": "this is an invalid body"}'

你应该会看到 HTTP/1.1 400 Bad Request 响应,其响应体类似于以下内容:

doesn't match schema #/components/schemas/Pet: Error at "/name": property "name" is missing
Schema:
{
"properties": {
"category": {
"$ref": "#/components/schemas/Category"
},
"id": {
"example": 10,
"format": "int64",
"type": "integer"
},
...
}

Value:
{
"invalid-body": "this is an invalid body"
}
| Error at "/photoUrls": property "photoUrls" is missing
Schema:
{
"properties": {
"category": {
"$ref": "#/components/schemas/Category"
},
...
}

Value:
{
"invalid-body": "this is an invalid body"
}

监控违规请求而不阻断流量

使用 reject_if_not_match 控制是不合规请求被阻止还是继续放行。此示例仅适用于 API7 企业版 3.9.6 及更高版本,不适用于 APISIX。

拒绝不符合规范的请求

reject_if_not_match 设置为 true(默认值)时,未通过 OAS 验证的请求会被拦截,并返回 HTTP/1.1 400 Bad Request 响应。

创建一个从 URL 获取规范的路由:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "body_validation",
"uri": "/*",
"plugins": {
"oas-validator": {
"spec": "'"${OPEN_API_SPEC}"'",
"reject_if_not_match": true
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"petstore3.swagger.io:443": 1
},
"scheme": "https",
"pass_host": "node"
}
}'

发送带有无效请求体的请求:

curl -i "http://127.0.0.1:9080/api/v3/pet" -X POST \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"invalid-body": "this is an invalid body"}'

你应该会看到 HTTP/1.1 400 Bad Request 响应,其响应体类似于以下内容:

{"message":"failed to validate request."}

允许不符合规范的请求通过

reject_if_not_match 设置为 false 时,不符合规范的请求不会被拦截,而是转发到上游,同时验证错误会记录到错误日志中。

更新路由,将 reject_if_not_match 设置为 false

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "body_validation",
"uri": "/*",
"plugins": {
"oas-validator": {
"spec": "'"${OPEN_API_SPEC}"'",
"reject_if_not_match": false
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"petstore3.swagger.io:443": 1
},
"scheme": "https",
"pass_host": "node"
}
}'

发送带有无效请求体的请求:

curl -i "http://127.0.0.1:9080/api/v3/pet" -X POST \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"invalid-body": "this is an invalid body"}'

你应该会看到 HTTP/1.1 500 Internal Server Error 响应,因为 petstore3.swagger.io 无法正确处理无效请求体。不过,可以确认该请求已成功转发到上游。

你还应该会看到类似以下的错误日志,其中记录了请求方法、URI 和验证错误:

[error] error occurred while validating request [POST /api/v3/pet], err: ...

这样便可以在不影响现有客户端的情况下,通过日志审计不合规流量。

使用远程规范 URL 进行验证

本示例适用于 API7 企业版 3.9.12 及更高版本。由于 Apache APISIX 尚不支持 spec_url 功能,本示例不适用于 Apache APISIX。

当 OpenAPI 规范过大而无法内嵌(spec 字段大小上限为 2 MB),或希望定期自动刷新规范时,请使用 spec_url 从远程 URL 加载规范。

创建一条从 URL 获取规范的路由:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "url_validation",
"uri": "/*",
"plugins": {
"oas-validator": {
"spec_url": "https://petstore3.swagger.io/api/v3/openapi.json",
"timeout": 5000
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"petstore3.swagger.io:443": 1
},
"scheme": "https",
"pass_host": "node"
}
}'

如果规范端点需要身份认证,请使用相同的配置结构,并将占位 URL、后端和 Token 替换为实际环境中的值:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "url_validation",
"uri": "/*",
"plugins": {
"oas-validator": {
"spec_url": "https://internal-api.example.com/openapi.json",
"spec_url_request_headers": { "Authorization": "Bearer <token>" },
"timeout": 5000
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"internal-api.example.com:443": 1
},
"scheme": "https",
"pass_host": "node"
}
}'

若要配置所获取规范的缓存 TTL(默认为 3600 秒),请设置插件元数据:

curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/oas-validator" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"spec_url_ttl": 1800
}'