oas-validator
oas-validator 插件可根据符合 OpenAPI v3 的既定 API 规范验证 HTTP 请求和响应。从 API7 企业版 3.9.8 起支持 OpenAPI 3.1,包括数值形式的 exclusiveMinimum/exclusiveMaximum、if/then/else 条件 Schema、通过 ["string", "null"] 定义的可空类型,以及 const、patternProperties、prefixItems 和 JSON Schema $dynamicRef/$dynamicAnchor 等功能。
示例
继续之前,请获取 Swagger Petstore 的 OpenAPI 规范,后续示例将使用该规范。
- Admin API
- ADC
- Ingress Controller
export OPEN_API_SPEC=$(curl -s "https://petstore3.swagger.io/api/v3/openapi.json" | sed 's/"/\\"/g')
export OPEN_API_SPEC=$(curl -s "https://petstore3.swagger.io/api/v3/openapi.json")
curl -s "https://petstore3.swagger.io/api/v3/openapi.json"
请在清单中将 <OPENAPI_SPEC> 替换为实际的 OpenAPI JSON。
验证请求体
此示例演示了如何根据给定规范验证请求体。
创建一个使用 OAS validator 插件的路由:
- Admin API
- ADC
- Ingress Controller
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"
}
}'
services:
- name: petstore
routes:
- uris:
- /*
name: body_validation
plugins:
oas-validator:
spec: "${OPEN_API_SPEC}"
upstream:
type: roundrobin
scheme: https
pass_host: node
nodes:
- host: petstore3.swagger.io
port: 443
weight: 1
将配置同步到网关:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: petstore-external-domain
spec:
type: ExternalName
externalName: petstore3.swagger.io
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: oas-validator-plugin-config
spec:
plugins:
- name: oas-validator
config:
spec: <OPENAPI_SPEC>
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: body-validation
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: oas-validator-plugin-config
backendRefs:
- name: petstore-external-domain
port: 443
---
apiVersion: apisix.apache.org/v1alpha1
kind: BackendTrafficPolicy
metadata:
namespace: aic
name: petstore-external-domain
spec:
targetRefs:
- group: ""
kind: Service
name: petstore-external-domain
passHost: node
scheme: https
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: petstore-external-domain
spec:
ingressClassName: apisix
scheme: https
passHost: node
externalNodes:
- type: Domain
name: petstore3.swagger.io
port: 443
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: body-validation
spec:
ingressClassName: apisix
http:
- name: body-validation
match:
paths:
- /*
upstreams:
- name: petstore-external-domain
plugins:
- name: oas-validator
config:
spec: <OPENAPI_SPEC>
应用配置:
kubectl apply -f oas-validator-ic.yaml
验证失败
使用不满足定义的 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 获取规范的路由:
- Admin API
- ADC
- Ingress Controller
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"
}
}'
services:
- name: petstore
routes:
- uris:
- /*
name: body_validation
plugins:
oas-validator:
spec: "${OPEN_API_SPEC}"
verbose_errors: true
upstream:
type: roundrobin
scheme: https
pass_host: node
nodes:
- host: petstore3.swagger.io
port: 443
weight: 1
将配置同步到网关:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: petstore-external-domain
spec:
type: ExternalName
externalName: petstore3.swagger.io
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: oas-validator-plugin-config
spec:
plugins:
- name: oas-validator
config:
spec: <OPENAPI_SPEC>
verbose_errors: true
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: body-validation
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: oas-validator-plugin-config
backendRefs:
- name: petstore-external-domain
port: 443
---
apiVersion: apisix.apache.org/v1alpha1
kind: BackendTrafficPolicy
metadata:
namespace: aic
name: petstore-external-domain
spec:
targetRefs:
- group: ""
kind: Service
name: petstore-external-domain
passHost: node
scheme: https
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: petstore-external-domain
spec:
ingressClassName: apisix
scheme: https
passHost: node
externalNodes:
- type: Domain
name: petstore3.swagger.io
port: 443
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: body-validation
spec:
ingressClassName: apisix
http:
- name: body-validation
match:
paths:
- /*
upstreams:
- name: petstore-external-domain
plugins:
- name: oas-validator
config:
spec: <OPENAPI_SPEC>
verbose_errors: true
应用配置:
kubectl apply -f oas-validator-ic.yaml
使用无效的请求体向上面创建的路由发送请求:
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 获取规范的路由:
- Admin API
- ADC
- Ingress Controller
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"
}
}'
services:
- name: petstore
routes:
- uris:
- /*
name: body_validation
plugins:
oas-validator:
spec: "${OPEN_API_SPEC}"
reject_if_not_match: true
upstream:
type: roundrobin
scheme: https
pass_host: node
nodes:
- host: petstore3.swagger.io
port: 443
weight: 1
将配置同步到网关:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: petstore-external-domain
spec:
type: ExternalName
externalName: petstore3.swagger.io
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: oas-validator-plugin-config
spec:
plugins:
- name: oas-validator
config:
spec: <OPENAPI_SPEC>
reject_if_not_match: true
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: body-validation
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: oas-validator-plugin-config
backendRefs:
- name: petstore-external-domain
port: 443
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: petstore-external-domain
spec:
ingressClassName: apisix
scheme: https
passHost: node
externalNodes:
- type: Domain
name: petstore3.swagger.io
port: 443
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: body-validation
spec:
ingressClassName: apisix
http:
- name: body-validation
match:
paths:
- /*
upstreams:
- name: petstore-external-domain
plugins:
- name: oas-validator
config:
spec: <OPENAPI_SPEC>
reject_if_not_match: true
应用配置:
kubectl apply -f oas-validator-ic.yaml
发送带有无效请求体的请求:
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:
- Admin API
- ADC
- Ingress Controller
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"
}
}'
services:
- name: petstore
routes:
- uris:
- /*
name: body_validation
plugins:
oas-validator:
spec: "${OPEN_API_SPEC}"
reject_if_not_match: false
upstream:
type: roundrobin
scheme: https
pass_host: node
nodes:
- host: petstore3.swagger.io
port: 443
weight: 1
将配置同步到网关:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: petstore-external-domain
spec:
type: ExternalName
externalName: petstore3.swagger.io
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: oas-validator-plugin-config
spec:
plugins:
- name: oas-validator
config:
spec: <OPENAPI_SPEC>
reject_if_not_match: false
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: body-validation
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: oas-validator-plugin-config
backendRefs:
- name: petstore-external-domain
port: 443
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: petstore-external-domain
spec:
ingressClassName: apisix
scheme: https
passHost: node
externalNodes:
- type: Domain
name: petstore3.swagger.io
port: 443
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: body-validation
spec:
ingressClassName: apisix
http:
- name: body-validation
match:
paths:
- /*
upstreams:
- name: petstore-external-domain
plugins:
- name: oas-validator
config:
spec: <OPENAPI_SPEC>
reject_if_not_match: false
应用配置:
kubectl apply -f oas-validator-ic.yaml
发送带有无效请求体的请求:
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 获取规范的路由:
- Admin API
- ADC
- Ingress Controller
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"
}
}'
services:
- name: petstore
routes:
- name: url-validation
uris:
- /*
plugins:
oas-validator:
spec_url: https://petstore3.swagger.io/api/v3/openapi.json
timeout: 5000
upstream:
type: roundrobin
nodes:
- host: petstore3.swagger.io
port: 443
weight: 1
scheme: https
pass_host: node
将配置同步到网关:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: petstore-external-domain
spec:
type: ExternalName
externalName: petstore3.swagger.io
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: oas-validator-plugin-config
spec:
plugins:
- name: oas-validator
config:
spec_url: https://petstore3.swagger.io/api/v3/openapi.json
timeout: 5000
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: url-validation
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: oas-validator-plugin-config
backendRefs:
- name: petstore-external-domain
port: 443
---
apiVersion: apisix.apache.org/v1alpha1
kind: BackendTrafficPolicy
metadata:
namespace: aic
name: petstore-external-domain
spec:
targetRefs:
- group: ""
kind: Service
name: petstore-external-domain
passHost: node
scheme: https
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: petstore-external-domain
spec:
ingressClassName: apisix
scheme: https
passHost: node
externalNodes:
- type: Domain
name: petstore3.swagger.io
port: 443
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: url-validation
spec:
ingressClassName: apisix
http:
- name: url-validation
match:
paths:
- /*
upstreams:
- name: petstore-external-domain
plugins:
- name: oas-validator
enable: true
config:
spec_url: https://petstore3.swagger.io/api/v3/openapi.json
timeout: 5000
应用配置:
kubectl apply -f oas-validator-ic.yaml
如果规范端点需要身份认证,请使用相同的配置结构,并将占位 URL、后端和 Token 替换为实际环境中的值:
- Admin API
- ADC
- Ingress Controller
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"
}
}'
services:
- name: internal-api
routes:
- name: url-validation
uris:
- /*
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:
- host: internal-api.example.com
port: 443
weight: 1
scheme: https
pass_host: node
将配置同步到网关:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: internal-api-external-domain
spec:
type: ExternalName
externalName: internal-api.example.com
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: oas-validator-plugin-config
spec:
plugins:
- name: oas-validator
config:
spec_url: https://internal-api.example.com/openapi.json
spec_url_request_headers:
Authorization: Bearer <token>
timeout: 5000
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: url-validation
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: oas-validator-plugin-config
backendRefs:
- name: internal-api-external-domain
port: 443
---
apiVersion: apisix.apache.org/v1alpha1
kind: BackendTrafficPolicy
metadata:
namespace: aic
name: internal-api-external-domain
spec:
targetRefs:
- group: ""
kind: Service
name: internal-api-external-domain
passHost: node
scheme: https
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: internal-api-external-domain
spec:
ingressClassName: apisix
scheme: https
passHost: node
externalNodes:
- type: Domain
name: internal-api.example.com
port: 443
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: url-validation
spec:
ingressClassName: apisix
http:
- name: url-validation
match:
paths:
- /*
upstreams:
- name: internal-api-external-domain
plugins:
- name: oas-validator
enable: true
config:
spec_url: https://internal-api.example.com/openapi.json
spec_url_request_headers:
Authorization: Bearer <token>
timeout: 5000
应用配置:
kubectl apply -f oas-validator-ic.yaml
若要配置所获取规范的缓存 TTL(默认为 3600 秒),请设置插件元数据:
- Admin API
- ADC
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
}'
plugin_metadata:
oas-validator:
spec_url_ttl: 1800
将配置同步到网关:
adc sync -f adc.yaml