@abiee/express-open-api 中文文档教程
Express OpenAPI
express 的另一个 OpenAPI 验证器。
Installation
您可以使用 npm
安装它:
npm install --save @abiee/express-open-api
或者使用 yarn
:
yarn add @abiee/express-open-api
Usage
您需要在某处有一个 OpenAPI 规范文件
const express = require('express');
const expressOpenAPI = require('@abiee/express-open-api');
const app = express();
const middleware = expressOpenAPI('/path/to/your/spec.yml');
app.get('/foo', middleware, (req, res) => {
res.send({ foo: 'bar' });
});
app.get('/xyz', middleware, (req, res) => {
res.send({ message: 'foobar' });
});
可以使用以下方式创建 valiator 中间件:
expressOpenAPI(pathToSpecFile, options);
Options
完整选项对象:
{
allowNotDefinedPaths: false,
allowNotDefinedResponses: false,
validateResponses: true,
invalidRequestHandler: (error, req, res, next) => next(), // ignore all errors
invalidResponseHandler: (error, body, req, res) => res.status(res.statusCode).send(body), // ignore all errors
onRequestValidationError: (error, method, endpoint) => console.log(error), // prints all errors
onResponseValidationError: (error, method, endpoint, statusCode) => console.log(error), // prints all errors
onMissingPath: (method, endpoint, error) => console.log(error), // prints all paths that are missed in the spec file
onMissingResponse: (method, endpoint, statusCode, error) => console.log(error) // prints all responses without OpenAPI spec
}
allowNotDefinedPaths
默认值: false
允许或不允许在 OpenAPI 规范文件中为快速路由定义路径。
当 false
时,它将强制在 OpenAPI 规范文件中为快速路由定义路径。 如果您的路由在 OpenAPI 规范文件中缺少路径,那么它将返回 400
错误。
{
"code": "ENDPOINT_NOT_DEFINED_IN_API_SPEC",
"method": "GET",
"endpoint": "/path/to/foo"
}
当 true
时,它将允许您拥有没有 OpenAPI 规范的路由。 换句话说,它将忽略 OpenAPI 规范文件中任何缺失的路径。 如果您有现成的 API 并且您希望在执行过程中逐步将路径添加到您的规范文件,这可能很有用。
allowNotDefinedResponses
默认值:false
当 express 返回响应时,允许或不在规范文件中定义响应。
当 false
时,它将强制在 OpenAPI 规范文件中为快速路由定义所有响应。 如果您的路线具有已定义的路线但缺少 OpenAPI 规范文件中的响应状态代码,则它将返回 501
错误。
{
"code": "RESPONSE_NOT_DEFINED_IN_API_SPEC",
"method": "GET",
"endpoint": "/path/to/foo",
"statusCode": 401
}
注意:中间件将首先查找状态代码,如果没有找到,则查找 default
响应。
validateResponses
默认值:true
是否验证响应。 当 false
时,验证器将忽略所有响应。 当 true
时,它将根据 OpenAPI 规范文件验证所有响应。 如果响应无效,则它将在定义时调用 invalidResponseHandler()
,或者使用默认错误处理程序返回 501
错误。
{
"code": "BAD_RESPONSE",
"errors": [{
// ... list of ajv errors
}]
}
invalidRequestHandler
可选
当在路由请求中发现错误时,您可以覆盖验证中间件的默认行为。 定义后,将使用以下签名调用该函数:
function errorHandler(error, req, res, next)
请注意,如果您的错误处理程序出现错误,则中间件将返回 500 Internal Server Error
。 在函数中,您可以定义处理错误所需的任何内容。 请注意,错误可能是这些类别中的任何一个:
ValidationError
RouteNotDefinedInOpenAPISpec
InvalidAPISpecFormat
- Generic errors
有关这些错误的更多详细信息,请参见下文。 如果您没有定义错误处理程序,则默认行为如下:
ValidationError
将是 400 Bad Request
错误。 使用负载:
{
"code": "BAD_REQUEST",
"errors": [{
// ... list of ajv errors
}]
}
RouteNotDefinedInOpenAPISpec
将是 400 Bad Request
负载:
{
"code": "ENDPOINT_NOT_DEFINED_IN_API_SPEC",
"method": "GET|POST|DELETE|PUT|...",
"endpont": "/path/to/endpoint"
}
InvalidAPISpecFormat
将是 500 Internal Server Error
with payload:
{
"code": "INVALID_API_SPEC_FORMAT",
"file": "/path/to/spec.yml".
"error": {
// ... swagger-parser error
}
}
发生意外错误时将出现 500 Internal Server Error
with payload:
{
"code": "INTERNAL_SERVER_ERROR",
"error": {
// ... serialized error to JSON
}
}
invalidResponseHandler
Optional
当在路由响应中发现错误时,您可以覆盖验证中间件的默认行为。 定义后,将使用以下签名调用该函数:
function errorHandler(error, body, req, res)
请注意,如果您的错误处理程序出现错误,则中间件将返回 500 Internal Server Error
。 在函数中,您可以定义处理错误所需的任何内容。 请注意,错误可能是这些类别中的任何一个:
ValidationError
ResponseNotDefinedInOpenAPISpec
InvalidAPISpecFormat
- Generic errors
有关这些错误的更多详细信息,请参见下文。 如果您没有定义错误处理程序,则默认行为如下:
ValidationError
将是 501 Not Implemented
错误。 负载:
{
"code": "BAD_RESPONSE",
"errors": [{
// ... list of ajv errors
}]
}
ResponseNotDefinedInOpenAPISpec
将是 501 Not Implemented
负载:
{
"code": "RESPONSE_NOT_DEFINED_IN_API_SPEC",
"method": "GET|POST|DELETE|PUT|...",
"endpont": "/path/to/endpoint",
"statusCode": 200|400|401|...
}
InvalidAPISpecFormat
将是 500 Internal Server Error
with payload:
{
"code": "INVALID_API_SPEC_FORMAT",
"file": "/path/to/spec.yml".
"error": {
// ... swagger-parser error
}
}
出现意外错误将是 500 Internal Server Error
with payload:
{
"code": "INTERNAL_SERVER_ERROR",
"error": {
// ... serialized error to JSON
}
}
onRequestValidationError
Optional
只要中间件在验证请求时发现错误,就会调用此函数。 您可以使用此功能记录错误或将它们发送到错误监控系统。
function onError(error, method, endpoint)
可能报告的错误:
ValidationError
RouteNotDefinedInOpenAPISpec
InvalidAPISpecFormat
注意:此函数抛出的任何错误都将被中间件忽略。
onResponseValidationError
可选
此函数将在中间件在验证响应时发现错误的任何时候调用。 您可以使用此功能记录错误或将它们发送到错误监控系统。
function onError(error, method, endpoint, statusCode)
可能报告的错误:
ValidationError
ResponseNotDefinedInOpenAPISpec
InvalidAPISpecFormat
注意:此函数抛出的任何错误都将被中间件忽略。
onMissingPath
可选
当没有为路由定义路径时将调用此函数。 这只是一个通知。 您可以使用此功能记录错误或将它们发送到错误监控系统。
function onMissingPath(method, endpoint, error)
onMissingResponse
可选
当没有为路由定义返回的响应时将调用此函数。 这只是一个通知。 您可以使用此功能记录错误或将它们发送到错误监控系统。
function onMissingResponse(method, endpoint, statusCode, error)
Errors
当验证中间件发生验证错误时,将抛出这些错误。 您可以通过以下方式导入错误:
const {
ValidationError,
RouteNotDefinedInOpenAPISpec,
ResponseNotDefinedInOpenAPISpec,
InvalidAPISpecFormat
} = require('@abiee/express-open-api/errors');
ValidationError
当 OpenAPI 规范文件中的 API 规范与给定路径中收到的请求不匹配时,就会发生此错误。 可用的公共方法是:
- getErrors(). Return a list of errors found in the request. It validates the request body and query parameters when available. The errors has the format defined by the AJV library.
RouteNotDefinedInOpenAPISpec
当 allowNotDefinedPaths
设置为 false
并且请求的路由未在 OpenAPI 规范文件的路径中定义时。 可用的公共方法是:
- getMethod(). HTTP method for the endpoint. Could be
GET
,POST
,DELETE
,PUT
, etc. - getEndpoint(). This is the full endpoint route.
InvalidAPISpecFormat
如果 OpenAPI 规范文件没有有效的 OpenAPI 格式,将抛出此错误。 可用的公共方法有:
- getFilePath(). The path to the main OpenAPI Spec file.
- getError(). The error returned by
swagger-parser
when validating the file.
Examples
以下是一些用例,展示了如何使用验证中间件。
Custom validation responses
您可能有自己的 API 标准,而默认的服务器响应正在破坏它们。 您可以根据需要更改默认响应:
const express = require('express');
const expressOpenAPI = require('@abiee/express-open-api');
const {
ValidationError,
RouteNotDefinedInOpenAPISpec,
ResponseNotDefinedInOpenAPISpec,
InvalidAPISpecFormat
} = require('@abiee/express-open-api/errors');
const app = express();
const middleware = expressOpenAPI('/path/to/your/spec.yml', {
invalidRequestHandler: (error, req, res, next) => {
if (error instanceof ValidationError) {
res.status(400).send({
status: 10001,
message: 'invalid request',
errorList: error.getErrors()
});
} else if (error instanceof InvalidAPISpecFormat) {
res.status(500).send({
status: 10010,
message: 'invalid api spec'
});
} else if (error instanceof RouteNotDefinedInOpenAPISpec) {
res.status(400).send({
status: 10012,
message: 'invalid route'
});
} else {
res.status(500).send({
status: 10000,
message: 'server error'
});
}
},
invalidResponseHandler: (error, body, req, res) => {
if (error instanceof ValidationError) {
res.status(400).send({
status: 10002,
message: 'invalid response',
errorList: error.getErrors()
});
} else if (error instanceof InvalidAPISpecFormat) {
res.status(500).send({
status: 10010,
message: 'invalid api spec'
});
} else if (error instanceof ResponseNotDefinedInOpenAPISpec) {
// ignore undefined specs
res.status(res.statusCode).send(body);
} else {
res.status(500).send({
status: 10000,
message: 'server error'
});
}
}
});
app.get('/foo', middleware, (req, res) => {
res.send({ foo: 'bar' });
});
app.get('/xyz', middleware, (req, res) => {
res.send({ message: 'foobar' });
});
Validate against OpenAPI but do not change actual API behaviour
您可以再次监控 OpenAPI 规范与实际 API 的合规性。 这对于 production
中的合同测试很有用,不会破坏任何东西。
const express = require('express');
const expressOpenAPI = require('@abiee/express-open-api');
const Sentry = require('@sentry/node');
const {
ValidationError,
RouteNotDefinedInOpenAPISpec,
ResponseNotDefinedInOpenAPISpec,
InvalidAPISpecFormat
} = require('@abiee/express-open-api/errors');
Sentry.init({ /* ... */ });
const app = express();
const middleware = expressOpenAPI('/path/to/your/spec.yml', {
allowNotDefinedPaths: true,
allowNotDefinedResponses: true,
invalidRequestHandler: (error, req, res, next) => {
if (error instanceof InvalidAPISpecFormat) {
return res.status(500).send({ code: 'INTERNAL_SERVER_ERROR' });
}
next(); // ignore all validation errors
},
invalidResponseHandler: (error, body, req, res) => {
res.status(res.statusCode).send(body); // ignore all validation errors
},
onRequestValidationError: (error, method, endpoint) => {
if (error instanceof ValidationError) {
Sentry.captureException(error);
}
},
onResponseValidationError: (error, method, endpoint, statusCode) => {
if (error instanceof ValidationError) {
Sentry.captureException(error);
}
}
});
app.get('/foo', middleware, (req, res) => {
res.send({ foo: 'bar' });
});
app.get('/xyz', middleware, (req, res) => {
res.send({ message: 'foobar' });
});
Express OpenAPI
Another OpenAPI Validator for express.
Installation
You can install this with npm
:
npm install --save @abiee/express-open-api
Or with yarn
:
yarn add @abiee/express-open-api
Usage
You need to have an OpenAPI Spec file somewhere
const express = require('express');
const expressOpenAPI = require('@abiee/express-open-api');
const app = express();
const middleware = expressOpenAPI('/path/to/your/spec.yml');
app.get('/foo', middleware, (req, res) => {
res.send({ foo: 'bar' });
});
app.get('/xyz', middleware, (req, res) => {
res.send({ message: 'foobar' });
});
The valiator middleware can be created with:
expressOpenAPI(pathToSpecFile, options);
Options
Full options object:
{
allowNotDefinedPaths: false,
allowNotDefinedResponses: false,
validateResponses: true,
invalidRequestHandler: (error, req, res, next) => next(), // ignore all errors
invalidResponseHandler: (error, body, req, res) => res.status(res.statusCode).send(body), // ignore all errors
onRequestValidationError: (error, method, endpoint) => console.log(error), // prints all errors
onResponseValidationError: (error, method, endpoint, statusCode) => console.log(error), // prints all errors
onMissingPath: (method, endpoint, error) => console.log(error), // prints all paths that are missed in the spec file
onMissingResponse: (method, endpoint, statusCode, error) => console.log(error) // prints all responses without OpenAPI spec
}
allowNotDefinedPaths
Default: false
Allow or not to have not defined paths in the OpenAPI spec file for express routes.
When false
it will enforce to have the path defined in the OpenAPI spec files for express routes. If you have a route with a missing path in the OpenAPI spec files then it will return a 400
error.
{
"code": "ENDPOINT_NOT_DEFINED_IN_API_SPEC",
"method": "GET",
"endpoint": "/path/to/foo"
}
When true
it will allow you to have routes without an OpenAPI spec. In other words, it will ignore any missing path in the OpenAPI spec files. This could be useful if you have en existent API and you want to incrementally add paths to your spec files as you go.
allowNotDefinedResponses
Default: false
Allow or no to have not defined respones in the spec file when express returns a response.
When false
it will enforce to have all responses defined in the OpenAPI spec files for express routes. If you have a route with a defined route but missing response status code in the OpenAPI spec files then it will return a 501
error.
{
"code": "RESPONSE_NOT_DEFINED_IN_API_SPEC",
"method": "GET",
"endpoint": "/path/to/foo",
"statusCode": 401
}
Note: the middleware will look uf for the status code first, if not found then will look up for a default
response.
validateResponses
Default: true
Validate responses or not. When false
all respones will be ignored by the validator. When true
it will validate all respones against the OpenAPI spec file. If a response is not valid then it will call invalidResponseHandler()
when defined, or will use the default error handler returning a 501
error.
{
"code": "BAD_RESPONSE",
"errors": [{
// ... list of ajv errors
}]
}
invalidRequestHandler
Optional
You may override the default behaviour of the validation middleware when an error is found in a request to a route. When defined, the function is called with the following signature:
function errorHandler(error, req, res, next)
Please note that if you error handler has an error then the middleware will return a 500 Internal Server Error
. In the function you can define anything you need to handle errors. Please be aware that error could be any of these classes:
ValidationError
RouteNotDefinedInOpenAPISpec
InvalidAPISpecFormat
- Generic errors
See below for more details about these errors. If you do not define an error handler the default behaviour is as follows:
ValidationError
will be a 400 Bad Request
error. With payload:
{
"code": "BAD_REQUEST",
"errors": [{
// ... list of ajv errors
}]
}
RouteNotDefinedInOpenAPISpec
will be a 400 Bad Request
error with payload:
{
"code": "ENDPOINT_NOT_DEFINED_IN_API_SPEC",
"method": "GET|POST|DELETE|PUT|...",
"endpont": "/path/to/endpoint"
}
InvalidAPISpecFormat
will be a 500 Internal Server Error
with payload:
{
"code": "INVALID_API_SPEC_FORMAT",
"file": "/path/to/spec.yml".
"error": {
// ... swagger-parser error
}
}
On unexpcted errors will be a 500 Internal Server Error
with payload:
{
"code": "INTERNAL_SERVER_ERROR",
"error": {
// ... serialized error to JSON
}
}
invalidResponseHandler
Optional
You may override the default behaviour of the validation middleware when an error is found in the response of a route. When defined, the function is called with the following signature:
function errorHandler(error, body, req, res)
Please note that if you error handler has an error then the middleware will return a 500 Internal Server Error
. In the function you can define anything you need to handle errors. Please be aware that error could be any of these classes:
ValidationError
ResponseNotDefinedInOpenAPISpec
InvalidAPISpecFormat
- Generic errors
See below for more details about these errors. If you do not define an error handler the default behaviour is as follows:
ValidationError
will be a 501 Not Implemented
error. With payload:
{
"code": "BAD_RESPONSE",
"errors": [{
// ... list of ajv errors
}]
}
ResponseNotDefinedInOpenAPISpec
will be a 501 Not Implemented
error with payload:
{
"code": "RESPONSE_NOT_DEFINED_IN_API_SPEC",
"method": "GET|POST|DELETE|PUT|...",
"endpont": "/path/to/endpoint",
"statusCode": 200|400|401|...
}
InvalidAPISpecFormat
will be a 500 Internal Server Error
with payload:
{
"code": "INVALID_API_SPEC_FORMAT",
"file": "/path/to/spec.yml".
"error": {
// ... swagger-parser error
}
}
On unexpcted errors will be a 500 Internal Server Error
with payload:
{
"code": "INTERNAL_SERVER_ERROR",
"error": {
// ... serialized error to JSON
}
}
onRequestValidationError
Optional
This function will be called anytime the middleware finds an error while validating request. You can use this function to log errors or send them to a error monitor system.
function onError(error, method, endpoint)
Errors that could be reported:
ValidationError
RouteNotDefinedInOpenAPISpec
InvalidAPISpecFormat
NOTE: Any error throwed by this function will be ignored by the middleware.
onResponseValidationError
Optional
This function will be called anytime the middleware finds an error while validating the response. You can use this function to log errors or send them to a error monitor system.
function onError(error, method, endpoint, statusCode)
Errors that could be reported:
ValidationError
ResponseNotDefinedInOpenAPISpec
InvalidAPISpecFormat
NOTE: Any error throwed by this function will be ignored by the middleware.
onMissingPath
Optional
This function will be called when a path is not defined for a route. This is just a notification. You can use this function to log errors or send them to a error monitor system.
function onMissingPath(method, endpoint, error)
onMissingResponse
Optional
This function will be called when a returned response is not defined for a route. This is just a notification. You can use this function to log errors or send them to a error monitor system.
function onMissingResponse(method, endpoint, statusCode, error)
Errors
Those errors will be throwed when a validation error occours in the validation middleware. You can import errors with:
const {
ValidationError,
RouteNotDefinedInOpenAPISpec,
ResponseNotDefinedInOpenAPISpec,
InvalidAPISpecFormat
} = require('@abiee/express-open-api/errors');
ValidationError
This errors happens when the API Specification in the OpenAPI Spec files does not match with the requested received in a given path. The available public methods are:
- getErrors(). Return a list of errors found in the request. It validates the request body and query parameters when available. The errors has the format defined by the AJV library.
RouteNotDefinedInOpenAPISpec
When allowNotDefinedPaths
is set to false
and the requested route is not defined in the paths of the OpenAPI spec files. The available public methods are:
- getMethod(). HTTP method for the endpoint. Could be
GET
,POST
,DELETE
,PUT
, etc. - getEndpoint(). This is the full endpoint route.
InvalidAPISpecFormat
If the OpenAPI Spec files has not a valid OpenAPI format this error will be throwed. The available public method are:
- getFilePath(). The path to the main OpenAPI Spec file.
- getError(). The error returned by
swagger-parser
when validating the file.
Examples
Here are some use cases that shows how the validation middleware can be used.
Custom validation responses
You may have your own API standards and the default server responses are breaking them. You can change the default responses as you need:
const express = require('express');
const expressOpenAPI = require('@abiee/express-open-api');
const {
ValidationError,
RouteNotDefinedInOpenAPISpec,
ResponseNotDefinedInOpenAPISpec,
InvalidAPISpecFormat
} = require('@abiee/express-open-api/errors');
const app = express();
const middleware = expressOpenAPI('/path/to/your/spec.yml', {
invalidRequestHandler: (error, req, res, next) => {
if (error instanceof ValidationError) {
res.status(400).send({
status: 10001,
message: 'invalid request',
errorList: error.getErrors()
});
} else if (error instanceof InvalidAPISpecFormat) {
res.status(500).send({
status: 10010,
message: 'invalid api spec'
});
} else if (error instanceof RouteNotDefinedInOpenAPISpec) {
res.status(400).send({
status: 10012,
message: 'invalid route'
});
} else {
res.status(500).send({
status: 10000,
message: 'server error'
});
}
},
invalidResponseHandler: (error, body, req, res) => {
if (error instanceof ValidationError) {
res.status(400).send({
status: 10002,
message: 'invalid response',
errorList: error.getErrors()
});
} else if (error instanceof InvalidAPISpecFormat) {
res.status(500).send({
status: 10010,
message: 'invalid api spec'
});
} else if (error instanceof ResponseNotDefinedInOpenAPISpec) {
// ignore undefined specs
res.status(res.statusCode).send(body);
} else {
res.status(500).send({
status: 10000,
message: 'server error'
});
}
}
});
app.get('/foo', middleware, (req, res) => {
res.send({ foo: 'bar' });
});
app.get('/xyz', middleware, (req, res) => {
res.send({ message: 'foobar' });
});
Validate against OpenAPI but do not change actual API behaviour
You may just monitor the compliance of your OpenAPI Specification agains the actual API. This could be useful for contract testing in production
without break anything.
const express = require('express');
const expressOpenAPI = require('@abiee/express-open-api');
const Sentry = require('@sentry/node');
const {
ValidationError,
RouteNotDefinedInOpenAPISpec,
ResponseNotDefinedInOpenAPISpec,
InvalidAPISpecFormat
} = require('@abiee/express-open-api/errors');
Sentry.init({ /* ... */ });
const app = express();
const middleware = expressOpenAPI('/path/to/your/spec.yml', {
allowNotDefinedPaths: true,
allowNotDefinedResponses: true,
invalidRequestHandler: (error, req, res, next) => {
if (error instanceof InvalidAPISpecFormat) {
return res.status(500).send({ code: 'INTERNAL_SERVER_ERROR' });
}
next(); // ignore all validation errors
},
invalidResponseHandler: (error, body, req, res) => {
res.status(res.statusCode).send(body); // ignore all validation errors
},
onRequestValidationError: (error, method, endpoint) => {
if (error instanceof ValidationError) {
Sentry.captureException(error);
}
},
onResponseValidationError: (error, method, endpoint, statusCode) => {
if (error instanceof ValidationError) {
Sentry.captureException(error);
}
}
});
app.get('/foo', middleware, (req, res) => {
res.send({ foo: 'bar' });
});
app.get('/xyz', middleware, (req, res) => {
res.send({ message: 'foobar' });
});