oas-validator
The oas-validator plugin allows validation of HTTP requests and responses against a defined API specification that complies with Open API v3 specification.
Examples
Before proceeding, fetch the Swagger Petstore Open API spec, which will be used in the following examples.
- 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"
You will replace <OPENAPI_SPEC> with the actual OpenAPI JSON in your manifest.
Validate Request Body
This example demonstrates validation of request body against a given specification.
Create the following route with the OAS validator plugin:
- 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
Synchronize the configuration to the gateway:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
Gateway API currently has a bug where the upstream scheme is not correctly configured. As a result, requests are forwarded over HTTP instead of HTTPS, which leads to the error The plain HTTP request was sent to HTTPS port.
This issue is scheduled to be fixed in API7 Ingress Controller in an upcoming release. Until then, this example cannot be completed using Gateway API.
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>
Apply the configuration:
kubectl apply -f oas-validator-ic.yaml
Failed Validation
Send a request to the above route with a request body that does not satisfy the defined Open API Spec:
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"}'
You should see an HTTP/1.1 400 Bad Request response with the response body similar to the following:
{"message":"failed to validate request."}
Successful Validation
Send a request to the route with a valid request body:
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"
}'
You should see an HTTP/1.1 200 OK response with the response body similar to the following:
{
"id": 1,
"category": { "id": 1, "name": "Dogs" },
"name": "doggie",
"photoUrls": ["string"],
"tags": [{ "id": 1, "name": "tag1" }],
"status": "available"
}
Get Verbose Error Response
This example demonstrates getting verbose error response when the validation fails.
Create a route with the OAS Validator plugin:
- 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
Synchronize the configuration to the gateway:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
Gateway API currently has a bug where the upstream scheme is not correctly configured. As a result, requests are forwarded over HTTP instead of HTTPS, which leads to the error The plain HTTP request was sent to HTTPS port.
This issue is scheduled to be fixed in API7 Ingress Controller in an upcoming release. Until then, this example cannot be completed using Gateway API.
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
Apply the configuration:
kubectl apply -f oas-validator-ic.yaml
Send a request to the route created above with an invalid request body:
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"}'
You should see an HTTP/1.1 400 Bad Request response with the response body similar to the following:
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"
}
Monitor Violations Without Blocking Traffic
Use reject_if_not_match to control whether non-compliant requests are blocked or allowed through. This example applies only to API7 Enterprise from version 3.9.6 and is not applicable in APISIX.
Reject Non-Compliant Requests
When reject_if_not_match is set to true (default), requests that fail OAS validation are blocked with a HTTP/1.1 400 Bad Request response.
Create a route with the OAS Validator plugin:
- 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
Synchronize the configuration to the gateway:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
Gateway API currently has a bug where the upstream scheme is not correctly configured. As a result, requests are forwarded over HTTP instead of HTTPS, which leads to the error The plain HTTP request was sent to HTTPS port.
This issue is scheduled to be fixed in API7 Ingress Controller in an upcoming release. Until then, this example cannot be completed using Gateway API.
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
Apply the configuration:
kubectl apply -f oas-validator-ic.yaml
Send a request with an invalid body:
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"}'
You should see an HTTP/1.1 400 Bad Request response with the response body similar to the following:
{"message":"failed to validate request."}
Allow Non-Compliant Requests to Pass
When reject_if_not_match is set to false, non-compliant requests are forwarded to the upstream instead of being blocked, and validation errors are recorded in the error logs.
Update the route to set reject_if_not_match to 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
Synchronize the configuration to the gateway:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
Gateway API currently has a bug where the upstream scheme is not correctly configured. As a result, requests are forwarded over HTTP instead of HTTPS, which leads to the error The plain HTTP request was sent to HTTPS port.
This issue is scheduled to be fixed in API7 Ingress Controller in an upcoming release. Until then, this example cannot be completed using Gateway API.
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
Apply the configuration:
kubectl apply -f oas-validator-ic.yaml
Send a request with an invalid body:
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"}'
You should see an HTTP/1.1 500 Internal Server Error response since petstore3.swagger.io does not correctly handle an invalid body. However, you can see that the request got passed successfully to the upstream.
You should also see an error log capturing the method, URI, and validation error similar to this:
[error] error occurred while validating request [POST /api/v3/pet], err: ...
This lets you audit non-compliant traffic in your logs without breaking existing clients.