@360mediadirect/express-openapi-validator 中文文档教程
express-openapi-validator
ExpressJS 的 OpenApi 验证器,自动验证 API 请求 和响应 使用 OpenAPI 3 规范。
????express-openapi-validator 是一个独立的库,可与新的和现有的 API 应用程序集成。 express-openapi-validator 让您可以按照自己的方式编写代码; 它不强加任何编码约定或项目布局。 只需将验证器安装到您的 Express 应用程序上,将其指向您的 OpenAPI 3 规范,然后按照您喜欢的方式定义和实施路由。 查看示例。
特性:
- ✔️ request validation
- ✔️ response validation (json only)
- security validation / custom security functions
- 3rd party / custom formats
- optionally auto-map OpenAPI endpoints to Express handler functions
- ✂️ \$ref support; split specs over multiple files
- file upload
Install
npm install express-openapi-validator
Usage
- Require/import the openapi validator
const OpenApiValidator = require('express-openapi-validator');
- Install the middleware
app.use(
OpenApiValidator.middleware({
apiSpec: './openapi.yaml',
validateRequests: true, // (default)
validateResponses: true, // false by default
}),
);
- Register an error handler
app.use((err, req, res, next) => {
// format error
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});
重要:确保 express 配置了所有相关的正文解析器。 主体解析器中间件函数必须在任何经过验证的路由之前指定。 查看示例。
Upgrading from 3.x
在 v4.xx 中,验证器作为标准连接中间件使用 app.use(...) 和/或 router.use(...)
安装(示例)。 这与 v3.xx 不同,后者需要 install
方法进行安装。 install
方法在 v4 中不再存在。
Usage (options)
请参阅高级用法选项:
- inline api specs as JSON.
- configure request/response validation options
- customize authentication with security validation
handlers
. - use OpenAPI 3.0.x 3rd party and custom formats.
- tweak the file upload configuration.
- ignore routes
- and more…
Example Express API Server
下面演示了如何使用 express-openapi-validator 自动验证请求和响应。 它还包括文件上传!
查看完整的源代码和OpenAPI spec 以下示例:
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const http = require('http');
const app = express();
// 1. Import the express-openapi-validator library
const OpenApiValidator = require('express-openapi-validator');
// 2. Set up body parsers for the request body types you expect
// Must be specified prior to endpoints in 5.
app.use(bodyParser.json());
app.use(bodyParser.text());
app.use(bodyParser.urlencoded({ extended: false }));
// 3. (optionally) Serve the OpenAPI spec
const spec = path.join(__dirname, 'api.yaml');
app.use('/spec', express.static(spec));
// 4. Install the OpenApiValidator onto your express app
app.use(
OpenApiValidator.middleware({
apiSpec: './api.yaml',
validateResponses: true, // <-- to validate responses
// unknownFormats: ['my-format'] // <-- to provide custom formats
}),
);
// 5. Define routes using Express
app.get('/v1/pets', function (req, res, next) {
res.json([
{ id: 1, type: 'cat', name: 'max' },
{ id: 2, type: 'cat', name: 'mini' },
]);
});
app.post('/v1/pets', function (req, res, next) {
res.json({ name: 'sparky', type: 'dog' });
});
app.get('/v1/pets/:id', function (req, res, next) {
res.json({ id: req.params.id, type: 'dog', name: 'sparky' });
});
// 5a. Define route(s) to upload file(s)
app.post('/v1/pets/:id/photos', function (req, res, next) {
// files are found in req.files
// non-file multipart params can be found as such: req.body['my-param']
res.json({
files_metadata: req.files.map((f) => ({
originalname: f.originalname,
encoding: f.encoding,
mimetype: f.mimetype,
// Buffer of file conents
buffer: f.buffer,
})),
});
});
// 6. Create an Express error handler
app.use((err, req, res, next) => {
// 7. Customize errors
console.error(err); // dump error to console for debug
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});
http.createServer(app).listen(3000);
Example Express API Server: with operationHandlers
不想手动将您的 OpenAPI 端点映射到快递处理函数? express-openapi-validator 可以自动为您完成!
使用 express-openapi-validator 的 OpenAPI x-eov-operation-*
供应商扩展。 查看带有源代码和OpenAPI 规范
这是要点< /strong>
- First, specifiy the
operationHandlers
option to set the base directory that contains your operation handler files.
app.use(
OpenApiValidator.middleware({
apiSpec,
operationHandlers: path.join(__dirname),
}),
);
- Next, use the
x-eov-operation-id
OpenAPI vendor extension oroperationId
to specify the id of operation handler to invoke.
/ping:
get:
# operationId: ping
x-eov-operation-id: ping
- Next, use the
x-eov-operation-handler
OpenAPI vendor extension to specify a path (relative tooperationHandlers
) to the module that contains the handler for this operation.
/ping:
get:
x-eov-operation-id: ping
x-eov-operation-handler: routes/ping # no .js or .ts extension
- Finally, create the express handler module e.g.
routes/ping.js
module.exports = {
// the express handler implementaiton for ping
ping: (req, res) => res.status(200).send('pong'),
};
注意:一个文件可能包含一个 或多个 处理程序。
以下是一些代码片段:
app.js
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const logger = require('morgan');
const http = require('http');
const OpenApiValidator = require('express-openapi-validator');
const port = 3000;
const app = express();
const apiSpec = path.join(__dirname, 'api.yaml');
// 1. Install bodyParsers for the request types your API will support
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.text());
app.use(bodyParser.json());
app.use(logger('dev'));
app.use('/spec', express.static(apiSpec));
// 2. Install the OpenApiValidator on your express app
app.use(
OpenApiValidator.middleware({
apiSpec,
validateResponses: true, // default false
// 3. Provide the base path to the operation handlers directory
operationHandlers: path.join(__dirname), // default false
}),
);
// 4. Woah sweet! With auto-wired operation handlers, I don't have to declare my routes!
// See api.yaml for x-eov-* vendor extensions
// 5. Create a custom error handler
app.use((err, req, res, next) => {
// format errors
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});
http.createServer(app).listen(port);
console.log(`Listening on port ${port}`);
module.exports = app;
api.yaml
/ping:
get:
description: |
ping then pong!
# OpenAPI's operationId may be used to to specify the operation id
operationId: ping
# x-eov-operation-id may be used to specify the operation id
# Used when operationId is omiited. Overrides operationId when both are specified
x-eov-operation-id: ping
# specifies the path to the operation handler.
# the path is relative to the operationHandlers option
# e.g. operations/base/path/routes/ping.js
x-eov-operation-handler: routes/ping
responses:
'200':
description: OK
# ...
ping.js
module.exports = {
// ping must match operationId or x-eov-operation-id above
// note that x-eov-operation-id overrides operationId
ping: (req, res) => res.status(200).send('pong'),
};
API Validation Response Examples
Validates a query parameter with a value constraint
curl -s http://localhost:3000/v1/pets/as |jq
{
"message": "request.params.id should be integer",
"errors": [
{
"path": ".params.id",
"message": "should be integer",
"errorCode": "type.openapi.validation"
}
]
}
Validates a query parameter with a range constraint
curl -s 'http://localhost:3000/v1/pets?limit=25' |jq
{
"message": "request.query should have required property 'type', request.query.limit should be <= 20",
"errors": [
{
"path": ".query.type",
"message": "should have required property 'type'",
"errorCode": "required.openapi.validation"
},
{
"path": ".query.limit",
"message": "should be <= 20",
"errorCode": "maximum.openapi.validation"
}
]
}
Validates securities e.g. API Key
curl -s --request POST \
--url http://localhost:3000/v1/pets \
--data '{}' |jq
{
"message": "'X-API-Key' header required",
"errors": [
{
"path": "/v1/pets",
"message": "'X-API-Key' header required"
}
]
}
提供标头通过 OpenAPI 验证。
注意:您的 Express 中间件或端点逻辑随后可以提供额外的检查。
curl -XPOST http://localhost:3000/v1/pets \
--header 'X-Api-Key: XXXXX' \
--header 'content-type: application/json' \
-d '{"name": "spot"}' | jq
{
"id": 4,
"name": "spot"
}
Validates content-type
curl -s --request POST \
--url http://localhost:3000/v1/pets \
--header 'content-type: application/xml' \
--header 'x-api-key: XXXX' \
--data '{
"name": "test"
}' |jq
"message": "unsupported media type application/xml",
"errors": [
{
"path": "/v1/pets",
"message": "unsupported media type application/xml"
}
]
}
Validates a POST request body
curl -s --request POST \
--url http://localhost:3000/v1/pets \
--header 'content-type: application/json' \
--header 'x-api-key: XXXX' \
--data '{}'|jq
{
"message": "request.body should have required property 'name'",
"errors": [
{
"path": ".body.name",
"message": "should have required property 'name'",
"errorCode": "required.openapi.validation"
}
]
}
File Upload (out of the box)
curl -XPOST http://localhost:3000/v1/pets/10/photos -F file=@app.js|jq
{
"files_metadata": [
{
"originalname": "app.js",
"encoding": "7bit",
"mimetype": "application/octet-stream"
}
]
}
Validates responses (optional)
响应验证错误返回 500
,而不是 400
不匹配的响应
curl -s 'http://localhost:3000/v1/pets/99' |jq
{
"message": ".response should have required property 'name', .response should have required property 'id'",
"errors": [
{
"path": ".response.name",
"message": "should have required property 'name'",
"errorCode": "required.openapi.validation"
},
{
"path": ".response.id",
"message": "should have required property 'id'",
"errorCode": "required.openapi.validation"
}
]
}
…and much more. Try it out!
Response status codes
/v1/pets/99
将返回与规范express-openapi -validator 根据情况返回以下错误代码。
Request validation (validateRequests=true)
status | when |
---|---|
400 (bad request) | a validation error is encountered |
401 (unauthorized) | a security / authentication errors is encountered e.g. missing api-key, Authorization header, etc |
404 (not found) | a path is not found i.e. not declared in the API spec |
405 (method not allowed) | a path is declared in the API spec, but a no schema is provided for the method |
Response validation (validateResponses=true)
status | when |
---|---|
500 (internal server error) | any error is encountered by the validator |
Advanced Usage
OpenApiValidator Middleware Options
express-openapi 验证器通过其选项提供了很大的灵活性。
选项通过选项对象提供。 选项采用以下形式:
OpenApiValidator.middleware({
apiSpec: './openapi.yaml',
validateRequests: true,
validateResponses: true,
validateSecurity: {
handlers: {
ApiKeyAuth: (req, scopes, schema) => {
throw { status: 401, message: 'sorry' }
}
}
},
validateFormats: 'fast',
formats: [{
name: 'my-custom-format',
type: 'string' | 'number',
validate: (value: any) => boolean,
}],
unknownFormats: ['phone-number', 'uuid'],
operationHandlers: false | 'operations/base/path' | { ... },
ignorePaths: /.*\/pets$/,
fileUploader: { ... } | true | false,
$refParser: {
mode: 'bundle'
}
});
▪️ apiSpec (required)
指定 OpenAPI 3 规范的路径或表示 OpenAPI 3 规范的 JSON 对象
apiSpec: './path/to/my-openapi-spec.yaml';
或
apiSpec: {
openapi: '3.0.1',
info: {...},
servers: [...],
paths: {...},
components: {
responses: {...},
schemas: {...}
}
}
▪️ validateRequests (optional)
确定验证器是否应验证请求。
true
(默认)- 验证请求。false
- 不验证请求。{ ... }
- 使用选项验证请求allowUnknownQueryParameters:
true
- 允许未知/未声明的查询参数通过验证false - (default) 如果存在未知查询参数则验证失败
例如:
validateRequests: {
allowUnknownQueryParameters: true;
}
allowUnknownQueryParameters
是为整个验证器设置的。 可以使用每次操作覆盖它 自定义属性 x-allow-unknown-query-parameters
。
例如,仅在单个端点上允许未知查询参数:
paths:
/allow_unknown:
get:
x-allow-unknown-query-parameters: true
parameters:
- name: value
in: query
schema:
type: string
responses:
200:
description: success
coerceTypes:
确定验证器是否将强制请求主体。 默认情况下,请求查询和路径参数、标头、cookie 是强制的,此设置不会影响它。
选项:
true
- 强制标量数据类型。false
-(默认)不强制类型。 (更严格、更安全)"array"
- 除了标量类型之间的强制转换之外,将标量数据强制转换为具有一个元素的数组,反之亦然(根据架构的要求)。例如:
validateRequests: {
coerceTypes: true;
}
▪️ validateResponses (optional)
确定验证器是否应验证响应。 还接受响应验证选项。
true
- 在“严格”模式下验证响应,即响应必须与模式匹配。false
(默认)- 不验证响应{ ... }
- 使用选项验证响应removeAdditional:
< code>"failing" - 架构验证失败的其他属性会自动从响应中删除。
coerceTypes:
true
- 强制标量数据类型。false
-(默认)不强制类型。 (几乎总是期望的行为)"array"
- 除了标量类型之间的强制转换,将标量数据强制转换为具有一个元素的数组,反之亦然(根据模式的要求)。例如:
validateResponses: {
removeAdditional: 'failing';
}
onError:
将在响应验证错误时调用的函数,而不是默认处理。 如果您想记录错误或发出指标,但又不想实际使请求失败,则很有用。 接收验证错误和有问题的响应正文。
例如:
validateResponses: {
onError: (error, body) => {
console.log(`Response body fails validation: `, error);
console.debug(body);
}
}
▪️ validateSecurity (optional)
确定验证器是否应验证安全性,例如 apikey、basic、oauth2、openid 等
true
(default)- 验证安全性false
-不验证安全性{ ... }
- 使用handlers
验证安全性。 请参阅 安全处理程序 文档。处理程序:
例如:
validateSecurity: {
handlers: {
ApiKeyAuth: function(req, scopes, schema) {
console.log('apikey handler throws custom error', scopes, schema);
throw Error('my message');
},
}
}
▪️ formats (optional)
定义客户格式列表。
[{ ... }]
- array of custom format objects. Each object must have the following properties:- name: string (required) - the format name
- validate: (v: any) => boolean (required) - the validation function
- type: 'string' | 'number' (optional) - the format's type
eg
formats: [
{
name: 'my-three-digit-format',
type: 'number',
// validate returns true the number has 3 digits, false otherwise
validate: (v) => /^\d{3}$/.test(v.toString()),
},
{
name: 'my-three-letter-format',
type: 'string',
// validate returns true the string has 3 letters, false otherwise
validate: (v) => /^[A-Za-z]{3}$/.test(v),
},
];
然后在规范中使用它 eg
my_property:
type: string
format: my-three-letter-format'
▪️ validateFormats (optional)
指定字符串格式验证的严格性。
"fast"
(default) - only validate syntax, but not semantics. E.g.2010-13-30T23:12:35Z
will pass validation eventhough it contains month 13."full"
- validate both syntax and semantics. Illegal dates will not pass.false
- do not validate formats at all.
▪️ unknownFormats (optional)
定义验证器在遇到未知或自定义格式时的行为方式。
true
(default) - When an unknown format is encountered, the validator will report a 400 error.[string]
(recommended for unknown formats) - An array of unknown format names that will be ignored by the validator. This option can be used to allow usage of third party schemas with format(s), but still fail if another unknown format is used. e.g.
unknownFormats: ['phone-number', 'uuid'];
"ignore"
- to log warning during schema compilation and always pass validation. This option is not recommended, as it allows to mistype format name and it won't be validated without any error message.
▪️ operationHandlers (optional)
定义操作处理程序的基本目录。 这与 express-openapi-validator 的 OpenAPI 供应商扩展、x-eov-operation-id
、x-eov-operation-handler
和 OpenAPI 的 operationId 结合使用。 请参阅示例。
此外,如果您想更改模块的解析方式,例如使用点分隔的操作 ID,例如 path.to.module.myFunction
,您可以选择添加自定义 resolver
。 请参阅文档和示例
string - 包含操作处理程序的基本目录
false
-(默认)禁用自动连接操作处理程序{ ... }
- 指定基本目录和可选的自定义解析器handlers:
例如:
operationHandlers: {
basePath: __dirname,
resolver: function (modulePath, route): express.RequestHandler {
///...
}
}
operationHandlers: 'operations/base/path'
注意x-eov-operation-handler
OpenAPI 供应商扩展指定了一个相对于operationHandlers
的路径. 因此,如果 operationHandlers
是 /handlers
并且 x-eov-operation-handler
具有路径 routes/ping
,那么使用处理程序文件 /handlers/routes/ping.js
(或 ts
)。
完整示例此处
api.yaml
/ping:
get:
description: |
ping then pong!
# OpenAPI's operationId may be used to to specify the operation id
operationId: ping
# x-eov-operation-id may be used to specify the operation id
# Used when operationId is omiited. Overrides operationId when both are specified
x-eov-operation-id: ping
# specifies the path to the operation handler.
# the path is relative to the operationHandlers option
# e.g. operations/base/path/routes/ping.js
x-eov-operation-handler: routes/ping
responses:
'200':
description: OK
# ...
routes/ping.js
x-eov-operation-handler
指定此处理程序文件的路径,ping.js
x- eov-operation-id
(或operationId
)指定操作处理程序的键,例如ping
module.exports = {
ping: (req, res) => res.status(200).send('pong'),
};
▪️ ignorePaths (optional)
定义一个正则表达式或函数来确定是否应忽略路径. 如果它是一个正则表达式,任何匹配正则表达式的路径都会被验证器忽略。 如果它是一个函数,它将忽略任何返回真值的路径。
以下忽略任何以 /pets
结尾的路径,例如 /v1/pets
。 作为正则表达式:
ignorePaths: /.*\/pets$/
或作为函数:
ignorePaths: (path) => path.endsWith('/pets')
▪️ fileUploader (optional)
指定传递给 multer 的选项。 express-openapi-validator 使用 multer 来处理文件上传。 请参阅 multer opts
true
(默认)- 启用 multer 并提供简单的文件(s) 上传功能false
- 禁用文件上传功能。 用户可以提供上传功能{...}
- 要传递给 multer 的 multer 选项。 查看 multer opts 了解可能的选项,例如
fileUploader: {
dest: 'uploads/';
}
▪️ \$refParser.mode (optional)
确定内部 json-schema-ref-parser 如何解析 JSON 模式引用。 通常,默认模式 bundle
就足够了,但是如果您在 \$refs 中使用 转义字符,取消引用
是必要的。
bundle
(default) - Bundles all referenced files/URLs into a single schema that only has internal $ref pointers. This eliminates the risk of circular references, but does not handle escaped characters in $refs.dereference
- Dereferences all $ref pointers in the JSON Schema, replacing each reference with its resolved value. Introduces risk of circular $refs. Handles escape characters in \$refs)
有关详细信息,请参阅此问题。
例如
$refParser: {
mode: 'bundle';
}
▪️ coerceTypes (optional) - deprecated
,确定验证器是否应强制值类型与 OpenAPI 规范中定义的值类型相匹配。 此选项仅应用于路径参数、查询字符串、标头和 cookie。 想要禁用此功能极不可能。 因此,此选项已被弃用,并将在下一个主要版本中删除
true
(default) - coerce scalar data types."array"
- in addition to coercions between scalar types, coerce scalar data to an array with one element and vice versa (as required by the schema).
The Base URL
验证器将仅验证请求、证券和响应 服务器的基本 URL。
当 API 和前端由同一个服务时,这很有用 应用。 (有关基本 URL 的更多详细信息。)
servers:
- url: https://api.example.com/v1
验证适用于在下面定义的所有路径这个基本网址。 您应用中的路线 _not_se URL(例如页面)将不会被验证。
URL | Validated? |
---|---|
https://api.example.com/v1/users | :whitecheckmark: |
https://api.example.com/index.html | no; not under the base URL |
在某些情况下,可能需要跳过验证 在基本 url 下 的路径。 为此,请使用 ignorePaths
选项。
Security handlers
注意:安全
处理程序
是一个可选组件。 securityhandlers
提供了一种便利,请求、声明的范围和安全模式本身作为参数提供给您定义的每个安全handlers
回调。 您在每个回调中编写的代码随后可以执行身份验证和授权检查。 请注意,同样可以使用标准 Express 中间件实现。 区别 是安全处理程序
为您提供规范中描述的 OpenAPI 架构数据。 最终,这意味着您不必在代码中复制该信息。总而言之,安全
handlers
完全是可选的,并且是为了方便而提供的。
安全处理程序指定一组自定义安全处理程序,用于验证安全性,即身份验证和授权。 如果指定了证券 handlers
对象,则必须为所有 证券定义一个处理程序。 如果未指定安全`处理程序,则始终使用默认处理程序。 默认处理程序将根据 OpenAPI 规范进行验证,然后调用下一个中间件。
如果指定了安全 handlers
,验证器将根据 OpenAPI 规范进行验证,然后调用安全处理程序,向其提供 Express 请求、安全范围和安全模式对象。
- security
handlers
is an object that maps security keys to security handler functions. Each security key must correspond tosecurityScheme
name. ThevalidateSecurity.handlers
object signature is as follows:
{
validateSecurity: {
handlers: {
[securityKey]: function(
req: Express.Request,
scopes: string[],
schema: SecuritySchemeObject
): void,
}
}
}
例如:
validateSecurity: {
handlers: {
ApiKeyAuth: function(req, scopes, schema) {
console.log('apikey handler throws custom error', scopes, schema);
throw Error('my message');
},
}
}
express-openapi-validator 在委托给安全处理程序之前执行基本的验证传递。 如果基本验证通过,则调用安全处理函数。
为了发出身份验证失败的信号,安全处理程序函数必须:
throw { status: 403, message: 'forbidden' }
throw Error('optional message')
return false
- return a promise which resolves to
false
e.gPromise.resolve(false)
- return a promise rejection e.g.
Promise.reject({ status: 401, message: 'yikes' });
Promise.reject(Error('optional 'message')
Promise.reject(false)
注意:返回错误状态401
,除非上面的选项i.
是使用
一些示例:
validateSecurity: {
handlers: {
ApiKeyAuth: (req, scopes, schema) => {
throw Error('my message');
},
OpenID: async (req, scopes, schema) => {
throw { status: 403, message: 'forbidden' }
},
BasicAuth: (req, scopes, schema) => {
return Promise.resolve(false);
},
...
}
}
为了授予authz,处理函数必须:
return true
- return a promise which resolves to
true
一些示例
validateSecurity: {
handlers: {
ApiKeyAuth: (req, scopes, schema) => {
return true;
},
BearerAuth: async (req, scopes, schema) => {
return true;
},
...
}
}
每个安全handlers
' securityKey
必须匹配 components/securitySchemes
属性
components:
securitySchemes:
ApiKeyAuth: # <-- Note this name must be used as the name handler function property
type: apiKey
in: header
name: X-API-Key
请参阅 OpenAPI 3 securityScheme
和 security
文档的身份验证 请参阅单元测试中的示例
Example: Multiple Validators and API specs
它可能对通过单个服务为具有不同规范的多个 API 提供服务。 一个示例可能是一个 API,它从同一服务为 v1
和 v2
提供服务。 下面的示例代码显示了如何实现这一点。
查看完整的示例
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const http = require('http');
const OpenApiValidator = require('express-openapi-validator');
app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.text());
app.use(bodyParser.json());
const versions = [1, 2];
for (const v of versions) {
const apiSpec = path.join(__dirname, `api.v${v}.yaml`);
app.use(
OpenApiValidator.middleware({
apiSpec,
}),
);
routes(app, v);
}
http.createServer(app).listen(3000);
console.log('Listening on port 3000');
function routes(app, v) {
if (v === 1) routesV1(app);
if (v === 2) routesV2(app);
}
function routesV1(app) {
const v = '/v1';
app.post(`${v}/pets`, (req, res, next) => {
res.json({ ...req.body });
});
app.get(`${v}/pets`, (req, res, next) => {
res.json([
{
id: 1,
name: 'happy',
type: 'cat',
},
]);
});
app.use((err, req, res, next) => {
// format error
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});
}
function routesV2(app) {
const v = '/v2';
app.get(`${v}/pets`, (req, res, next) => {
res.json([
{
pet_id: 1,
pet_name: 'happy',
pet_type: 'kitty',
},
]);
});
app.post(`${v}/pets`, (req, res, next) => {
res.json({ ...req.body });
});
app.use((err, req, res, next) => {
// format error
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});
}
module.exports = app;
FAQ
问: 如何匹配路径,如 RFC-6570 中所述?
答:OpenAPI 3.0 不支持 RFC-6570。 也就是说,我们提供了一种在语法上符合 OpenAPI 3 并完成常见用例的简约机制。 例如,匹配文件路径并将匹配的路径存储在 req.params
使用以下 OpenAPI 3.x 定义
/files/{path}*:
get:
parameters:
- name: path
in: path
required: true
schema:
type: string
使用以下 Express 路由定义
app.get(`/files/:path(*)`, (req, res) => { /* do stuff */ }`
A path like /files/some/long/path
将通过验证。 Express req.params.path
属性将保存值 some/long/path
。
问: 我可以将鉴别器与 oneOf
和 anyOf
一起使用吗?
答: 目前,支持顶级鉴别器。 请参阅顶级鉴别器示例
Q : securityHandlers
属性发生了什么变化?
答:在 v3 中,securityHandlers
已被 validateSecurity.handlers
取代。 要使用 v3 安全处理程序,请将现有的安全处理程序移至新属性。 不需要进行其他更改。 请注意 v2 securityHandlers
属性在 v3 中受支持,但已弃用
问:multerOpts
属性发生了什么变化?
A:在 v3 中,multerOpts
已被 fileUploader
取代。 为了使用 v3 fileUploader
,将您的 multer 选项移动到 fileUploader
不需要其他更改。 请注意 v2 multerOpts
属性在 v3 中受支持,但已弃用
Q: 我可以使用 allowUnknownQueryParameters: false
禁止未知查询参数。 如何禁止未知的身体参数?
A: 在描述additionalProperties: false > 例如 requestBody
以确保不允许使用其他属性。 例如:
Pet:
additionalProperties: false
required:
- name
properties:
name:
type: string
type:
type: string
问:我从 v2 升级到 v3,验证不再有效。 我如何解决它?
A:在2.xx版本中,install
方法是同步执行的,在3.x版本中是异步执行的。 要在 v3 中获得 v2 行为,请使用 installSync
方法。 有关详细信息,请参阅同步部分。
问: 我可以将 express-openapi-validator
与 swagger-ui-express
一起使用吗?
答:是的。 在安装 OpenApiValidator
之前,请务必使用
swagger-ui-express
服务中间件。 这将确保 swagger-ui-express
能够在 OpenApiValidator 尝试使用它之前充分准备规范。 例如:
const swaggerUi = require('swagger-ui-express')
const OpenApiValidator = require('express-openapi-validator')
...
app.use('/', swaggerUi.serve, swaggerUi.setup(documentation))
app.use(OpenApiValidator.middleware({
apiSpec, // api spec JSON object
//... other options
}
}))
问: 我在 express.Router
上定义了一个处理函数。 如果我调用 req.params
,每个参数值的类型都是 string
。 如果我在 express.Application
上定义了相同的处理函数,则 req.params
中的每个值都已被强制转换为我的规范中声明的类型。 为什么不在 express.Router
上强制使用这些 F 值?
答:首先,请务必注意此行为不会影响验证。 验证器将根据您的规范中定义的类型进行验证。
为了修改 req.params
,express 需要注册一个参数处理程序,例如 app.param(...)
或 router.param(.. .)。 由于
app
可用于中间件函数,验证器注册一个 app.param
处理程序来强制和修改 req.params
的值到它们声明的类型。 不幸的是,express 不提供从中间件函数确定当前路由器的方法,因此验证器无法在 express 路由器上注册相同的参数处理程序。 最终,这意味着如果您的处理程序函数是在 app
上定义的,则 req.params
的值将被强制为其声明的类型。 如果您的处理程序函数是在 express.Router
上声明的,则 req.params
值的值将是 string
类型(您必须强制转换它们例如 parseInt(req.params.id)
)。
Contributors ✨
欢迎投稿! 以下是如何贡献。
感谢这些优秀的人 (emoji key):
这个项目遵循 all-contributors 规范。 欢迎任何形式的贡献!
Community articles, blogs, and tutorials
正在寻找内容创作者......
您是否撰写过使用 express-openapi-validator
的文章、博客或教程?
请将您的链接发布到此处
我们计划在这里公开各种链接。
License
???? express-openapi-validator
An OpenApi validator for ExpressJS that automatically validates API requests and responses using an OpenAPI 3 specification.
????express-openapi-validator is an unopinionated library that integrates with new and existing API applications. express-openapi-validator lets you write code the way you want; it does not impose any coding convention or project layout. Simply, install the validator onto your express app, point it to your OpenAPI 3 specification, then define and implement routes the way you prefer. See an example.
Features:
- ✔️ request validation
- ✔️ response validation (json only)
- ???? security validation / custom security functions
- ???? 3rd party / custom formats
- ???? optionally auto-map OpenAPI endpoints to Express handler functions
- ✂️ \$ref support; split specs over multiple files
- ???? file upload
Install
npm install express-openapi-validator
Usage
- Require/import the openapi validator
const OpenApiValidator = require('express-openapi-validator');
- Install the middleware
app.use(
OpenApiValidator.middleware({
apiSpec: './openapi.yaml',
validateRequests: true, // (default)
validateResponses: true, // false by default
}),
);
- Register an error handler
app.use((err, req, res, next) => {
// format error
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});
Important: Ensure express is configured with all relevant body parsers. Body parser middleware functions must be specified prior to any validated routes. See an example.
Upgrading from 3.x
In v4.x.x, the validator is installed as standard connect middleware using app.use(...) and/or router.use(...)
(example). This differs from the v3.x.x the installation which required the install
method(s). The install
methods no longer exist in v4.
Usage (options)
See Advanced Usage options to:
- inline api specs as JSON.
- configure request/response validation options
- customize authentication with security validation
handlers
. - use OpenAPI 3.0.x 3rd party and custom formats.
- tweak the file upload configuration.
- ignore routes
- and more…
Example Express API Server
The following demonstrates how to use express-openapi-validator to auto validate requests and responses. It also includes file upload!
See the complete source code and OpenAPI spec for the example below:
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const http = require('http');
const app = express();
// 1. Import the express-openapi-validator library
const OpenApiValidator = require('express-openapi-validator');
// 2. Set up body parsers for the request body types you expect
// Must be specified prior to endpoints in 5.
app.use(bodyParser.json());
app.use(bodyParser.text());
app.use(bodyParser.urlencoded({ extended: false }));
// 3. (optionally) Serve the OpenAPI spec
const spec = path.join(__dirname, 'api.yaml');
app.use('/spec', express.static(spec));
// 4. Install the OpenApiValidator onto your express app
app.use(
OpenApiValidator.middleware({
apiSpec: './api.yaml',
validateResponses: true, // <-- to validate responses
// unknownFormats: ['my-format'] // <-- to provide custom formats
}),
);
// 5. Define routes using Express
app.get('/v1/pets', function (req, res, next) {
res.json([
{ id: 1, type: 'cat', name: 'max' },
{ id: 2, type: 'cat', name: 'mini' },
]);
});
app.post('/v1/pets', function (req, res, next) {
res.json({ name: 'sparky', type: 'dog' });
});
app.get('/v1/pets/:id', function (req, res, next) {
res.json({ id: req.params.id, type: 'dog', name: 'sparky' });
});
// 5a. Define route(s) to upload file(s)
app.post('/v1/pets/:id/photos', function (req, res, next) {
// files are found in req.files
// non-file multipart params can be found as such: req.body['my-param']
res.json({
files_metadata: req.files.map((f) => ({
originalname: f.originalname,
encoding: f.encoding,
mimetype: f.mimetype,
// Buffer of file conents
buffer: f.buffer,
})),
});
});
// 6. Create an Express error handler
app.use((err, req, res, next) => {
// 7. Customize errors
console.error(err); // dump error to console for debug
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});
http.createServer(app).listen(3000);
Example Express API Server: with operationHandlers
Don't want to manually map your OpenAPI endpoints to Express handler functions? express-openapi-validator can do it for you, automatically!
Use express-openapi-validator's OpenAPI x-eov-operation-*
vendor extensions. See a full example with source code and an OpenAPI spec
Here's the gist
- First, specifiy the
operationHandlers
option to set the base directory that contains your operation handler files.
app.use(
OpenApiValidator.middleware({
apiSpec,
operationHandlers: path.join(__dirname),
}),
);
- Next, use the
x-eov-operation-id
OpenAPI vendor extension oroperationId
to specify the id of operation handler to invoke.
/ping:
get:
# operationId: ping
x-eov-operation-id: ping
- Next, use the
x-eov-operation-handler
OpenAPI vendor extension to specify a path (relative tooperationHandlers
) to the module that contains the handler for this operation.
/ping:
get:
x-eov-operation-id: ping
x-eov-operation-handler: routes/ping # no .js or .ts extension
- Finally, create the express handler module e.g.
routes/ping.js
module.exports = {
// the express handler implementaiton for ping
ping: (req, res) => res.status(200).send('pong'),
};
Note: A file may contain one or many handlers.
Below are some code snippets:
app.js
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const logger = require('morgan');
const http = require('http');
const OpenApiValidator = require('express-openapi-validator');
const port = 3000;
const app = express();
const apiSpec = path.join(__dirname, 'api.yaml');
// 1. Install bodyParsers for the request types your API will support
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.text());
app.use(bodyParser.json());
app.use(logger('dev'));
app.use('/spec', express.static(apiSpec));
// 2. Install the OpenApiValidator on your express app
app.use(
OpenApiValidator.middleware({
apiSpec,
validateResponses: true, // default false
// 3. Provide the base path to the operation handlers directory
operationHandlers: path.join(__dirname), // default false
}),
);
// 4. Woah sweet! With auto-wired operation handlers, I don't have to declare my routes!
// See api.yaml for x-eov-* vendor extensions
// 5. Create a custom error handler
app.use((err, req, res, next) => {
// format errors
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});
http.createServer(app).listen(port);
console.log(`Listening on port ${port}`);
module.exports = app;
api.yaml
/ping:
get:
description: |
ping then pong!
# OpenAPI's operationId may be used to to specify the operation id
operationId: ping
# x-eov-operation-id may be used to specify the operation id
# Used when operationId is omiited. Overrides operationId when both are specified
x-eov-operation-id: ping
# specifies the path to the operation handler.
# the path is relative to the operationHandlers option
# e.g. operations/base/path/routes/ping.js
x-eov-operation-handler: routes/ping
responses:
'200':
description: OK
# ...
ping.js
module.exports = {
// ping must match operationId or x-eov-operation-id above
// note that x-eov-operation-id overrides operationId
ping: (req, res) => res.status(200).send('pong'),
};
API Validation Response Examples
Validates a query parameter with a value constraint
curl -s http://localhost:3000/v1/pets/as |jq
{
"message": "request.params.id should be integer",
"errors": [
{
"path": ".params.id",
"message": "should be integer",
"errorCode": "type.openapi.validation"
}
]
}
Validates a query parameter with a range constraint
curl -s 'http://localhost:3000/v1/pets?limit=25' |jq
{
"message": "request.query should have required property 'type', request.query.limit should be <= 20",
"errors": [
{
"path": ".query.type",
"message": "should have required property 'type'",
"errorCode": "required.openapi.validation"
},
{
"path": ".query.limit",
"message": "should be <= 20",
"errorCode": "maximum.openapi.validation"
}
]
}
Validates securities e.g. API Key
curl -s --request POST \
--url http://localhost:3000/v1/pets \
--data '{}' |jq
{
"message": "'X-API-Key' header required",
"errors": [
{
"path": "/v1/pets",
"message": "'X-API-Key' header required"
}
]
}
Providing the header passes OpenAPI validation.
Note: that your Express middleware or endpoint logic can then provide additional checks.
curl -XPOST http://localhost:3000/v1/pets \
--header 'X-Api-Key: XXXXX' \
--header 'content-type: application/json' \
-d '{"name": "spot"}' | jq
{
"id": 4,
"name": "spot"
}
Validates content-type
curl -s --request POST \
--url http://localhost:3000/v1/pets \
--header 'content-type: application/xml' \
--header 'x-api-key: XXXX' \
--data '{
"name": "test"
}' |jq
"message": "unsupported media type application/xml",
"errors": [
{
"path": "/v1/pets",
"message": "unsupported media type application/xml"
}
]
}
Validates a POST request body
curl -s --request POST \
--url http://localhost:3000/v1/pets \
--header 'content-type: application/json' \
--header 'x-api-key: XXXX' \
--data '{}'|jq
{
"message": "request.body should have required property 'name'",
"errors": [
{
"path": ".body.name",
"message": "should have required property 'name'",
"errorCode": "required.openapi.validation"
}
]
}
File Upload (out of the box)
curl -XPOST http://localhost:3000/v1/pets/10/photos -F file=@app.js|jq
{
"files_metadata": [
{
"originalname": "app.js",
"encoding": "7bit",
"mimetype": "application/octet-stream"
}
]
}
Validates responses (optional)
Errors in response validation return 500
, not of 400
/v1/pets/99
will return a response that does not match the spec
curl -s 'http://localhost:3000/v1/pets/99' |jq
{
"message": ".response should have required property 'name', .response should have required property 'id'",
"errors": [
{
"path": ".response.name",
"message": "should have required property 'name'",
"errorCode": "required.openapi.validation"
},
{
"path": ".response.id",
"message": "should have required property 'id'",
"errorCode": "required.openapi.validation"
}
]
}
…and much more. Try it out!
Response status codes
express-openapi-validator returns the following error codes depending on the situation.
Request validation (validateRequests=true)
status | when |
---|---|
400 (bad request) | a validation error is encountered |
401 (unauthorized) | a security / authentication errors is encountered e.g. missing api-key, Authorization header, etc |
404 (not found) | a path is not found i.e. not declared in the API spec |
405 (method not allowed) | a path is declared in the API spec, but a no schema is provided for the method |
Response validation (validateResponses=true)
status | when |
---|---|
500 (internal server error) | any error is encountered by the validator |
Advanced Usage
OpenApiValidator Middleware Options
express-openapi validator provides a good deal of flexibility via its options.
Options are provided via the options object. Options take the following form:
OpenApiValidator.middleware({
apiSpec: './openapi.yaml',
validateRequests: true,
validateResponses: true,
validateSecurity: {
handlers: {
ApiKeyAuth: (req, scopes, schema) => {
throw { status: 401, message: 'sorry' }
}
}
},
validateFormats: 'fast',
formats: [{
name: 'my-custom-format',
type: 'string' | 'number',
validate: (value: any) => boolean,
}],
unknownFormats: ['phone-number', 'uuid'],
operationHandlers: false | 'operations/base/path' | { ... },
ignorePaths: /.*\/pets$/,
fileUploader: { ... } | true | false,
$refParser: {
mode: 'bundle'
}
});
▪️ apiSpec (required)
Specifies the path to an OpenAPI 3 specification or a JSON object representing the OpenAPI 3 specificiation
apiSpec: './path/to/my-openapi-spec.yaml';
or
apiSpec: {
openapi: '3.0.1',
info: {...},
servers: [...],
paths: {...},
components: {
responses: {...},
schemas: {...}
}
}
▪️ validateRequests (optional)
Determines whether the validator should validate requests.
true
(default) - validate requests.false
- do not validate requests.{ ... }
- validate requests with optionsallowUnknownQueryParameters:
true
- enables unknown/undeclared query parameters to pass validationfalse
- (default) fail validation if an unknown query parameter is presentFor example:
validateRequests: {
allowUnknownQueryParameters: true;
}
allowUnknownQueryParameters
is set for the entire validator. It can be overwritten per-operation using a custom property x-allow-unknown-query-parameters
.
For example to allow unknown query parameters on ONLY a single endpoint:
paths:
/allow_unknown:
get:
x-allow-unknown-query-parameters: true
parameters:
- name: value
in: query
schema:
type: string
responses:
200:
description: success
coerceTypes:
Determines whether the validator will coerce the request body. Request query and path params, headers, cookies are coerced by default and this setting does not affect that.
Options:
true
- coerce scalar data types.false
- (default) do not coerce types. (more strict, safer)"array"
- in addition to coercions between scalar types, coerce scalar data to an array with one element and vice versa (as required by the schema).For example:
validateRequests: {
coerceTypes: true;
}
▪️ validateResponses (optional)
Determines whether the validator should validate responses. Also accepts response validation options.
true
- validate responses in 'strict' mode i.e. responses MUST match the schema.false
(default) - do not validate responses{ ... }
- validate responses with optionsremoveAdditional:
"failing"
- additional properties that fail schema validation are automatically removed from the response.coerceTypes:
true
- coerce scalar data types.false
- (default) do not coerce types. (almost always the desired behavior)"array"
- in addition to coercions between scalar types, coerce scalar data to an array with one element and vice versa (as required by the schema).For example:
validateResponses: {
removeAdditional: 'failing';
}
onError:
A function that will be invoked on response validation error, instead of the default handling. Useful if you want to log an error or emit a metric, but don't want to actually fail the request. Receives the validation error and offending response body.
For example:
validateResponses: {
onError: (error, body) => {
console.log(`Response body fails validation: `, error);
console.debug(body);
}
}
▪️ validateSecurity (optional)
Determines whether the validator should validate securities e.g. apikey, basic, oauth2, openid, etc
true
(default) - validate securityfalse
- do not validate security{ ... }
- validate security withhandlers
. See Security handlers doc.handlers:
For example:
validateSecurity: {
handlers: {
ApiKeyAuth: function(req, scopes, schema) {
console.log('apikey handler throws custom error', scopes, schema);
throw Error('my message');
},
}
}
▪️ formats (optional)
Defines a list of custome formats.
[{ ... }]
- array of custom format objects. Each object must have the following properties:- name: string (required) - the format name
- validate: (v: any) => boolean (required) - the validation function
- type: 'string' | 'number' (optional) - the format's type
e.g.
formats: [
{
name: 'my-three-digit-format',
type: 'number',
// validate returns true the number has 3 digits, false otherwise
validate: (v) => /^\d{3}$/.test(v.toString()),
},
{
name: 'my-three-letter-format',
type: 'string',
// validate returns true the string has 3 letters, false otherwise
validate: (v) => /^[A-Za-z]{3}$/.test(v),
},
];
Then use it in a spec e.g.
my_property:
type: string
format: my-three-letter-format'
▪️ validateFormats (optional)
Specifies the strictness of validation of string formats.
"fast"
(default) - only validate syntax, but not semantics. E.g.2010-13-30T23:12:35Z
will pass validation eventhough it contains month 13."full"
- validate both syntax and semantics. Illegal dates will not pass.false
- do not validate formats at all.
▪️ unknownFormats (optional)
Defines how the validator should behave if an unknown or custom format is encountered.
true
(default) - When an unknown format is encountered, the validator will report a 400 error.[string]
(recommended for unknown formats) - An array of unknown format names that will be ignored by the validator. This option can be used to allow usage of third party schemas with format(s), but still fail if another unknown format is used. e.g.
unknownFormats: ['phone-number', 'uuid'];
"ignore"
- to log warning during schema compilation and always pass validation. This option is not recommended, as it allows to mistype format name and it won't be validated without any error message.
▪️ operationHandlers (optional)
Defines the base directory for operation handlers. This is used in conjunction with express-openapi-validator's OpenAPI vendor extensions, x-eov-operation-id
, x-eov-operation-handler
and OpenAPI's operationId
. See example.
Additionally, if you want to change how modules are resolved e.g. use dot deliminted operation ids e.g. path.to.module.myFunction
, you may optionally add a custom resolver
. See documentation and example
string
- the base directory containing operation handlersfalse
- (default) disable auto wired operation handlers{ ... }
- specifies a base directory and optionally a custom resolverhandlers:
For example:
operationHandlers: {
basePath: __dirname,
resolver: function (modulePath, route): express.RequestHandler {
///...
}
}
operationHandlers: 'operations/base/path'
Note that the x-eov-operation-handler
OpenAPI vendor extension specifies a path relative to operationHandlers
. Thus if operationHandlers
is /handlers
and an x-eov-operation-handler
has path routes/ping
, then the handler file /handlers/routes/ping.js
(or ts
) is used.
Complete example here
api.yaml
/ping:
get:
description: |
ping then pong!
# OpenAPI's operationId may be used to to specify the operation id
operationId: ping
# x-eov-operation-id may be used to specify the operation id
# Used when operationId is omiited. Overrides operationId when both are specified
x-eov-operation-id: ping
# specifies the path to the operation handler.
# the path is relative to the operationHandlers option
# e.g. operations/base/path/routes/ping.js
x-eov-operation-handler: routes/ping
responses:
'200':
description: OK
# ...
routes/ping.js
x-eov-operation-handler
specifies the path to this handlers file, ping.js
x-eov-operation-id
(or operationId
) specifies operation handler's key e.g. ping
module.exports = {
ping: (req, res) => res.status(200).send('pong'),
};
▪️ ignorePaths (optional)
Defines a regular expression or function that determines whether a path(s) should be ignored. If it's a regular expression, any path that matches the regular expression will be ignored by the validator. If it's a function, it will ignore any paths that returns a truthy value.
The following ignores any path that ends in /pets
e.g. /v1/pets
. As a regular expression:
ignorePaths: /.*\/pets$/
or as a function:
ignorePaths: (path) => path.endsWith('/pets')
▪️ fileUploader (optional)
Specifies the options to passthrough to multer. express-openapi-validator uses multer to handle file uploads. see multer opts
true
(default) - enables multer and provides simple file(s) upload capabilitiesfalse
- disables file upload capability. Upload capabilities may be provided by the user{...}
- multer options to be passed-through to multer. see multer opts for possible optionse.g.
fileUploader: {
dest: 'uploads/';
}
▪️ \$refParser.mode (optional)
Determines how JSON schema references are resolved by the internal json-schema-ref-parser. Generally, the default mode, bundle
is sufficient, however if you use escape characters in \$refs, dereference
is necessary.
bundle
(default) - Bundles all referenced files/URLs into a single schema that only has internal $ref pointers. This eliminates the risk of circular references, but does not handle escaped characters in $refs.dereference
- Dereferences all $ref pointers in the JSON Schema, replacing each reference with its resolved value. Introduces risk of circular $refs. Handles escape characters in \$refs)
See this issue for more information.
e.g.
$refParser: {
mode: 'bundle';
}
▪️ coerceTypes (optional) - deprecated
Determines whether the validator should coerce value types to match the those defined in the OpenAPI spec. This option applies only to path params, query strings, headers, and cookies. It is highly unlikley that will want to disable this. As such this option is deprecated and will be removed in the next major version
true
(default) - coerce scalar data types."array"
- in addition to coercions between scalar types, coerce scalar data to an array with one element and vice versa (as required by the schema).
The Base URL
The validator will only validate requests, securities, and responses that are under the server's base URL.
This is useful for those times when the API and frontend are being served by the same application. (More detail about the base URL.)
servers:
- url: https://api.example.com/v1
The validation applies to all paths defined under this base URL. Routes in your app that are _not_se URL—such as pages—will not be validated.
URL | Validated? |
---|---|
https://api.example.com/v1/users | :whitecheckmark: |
https://api.example.com/index.html | no; not under the base URL |
In some cases, it may be necessary to skip validation for paths under the base url. To do this, use the ignorePaths
option.
Security handlers
Note: security
handlers
are an optional component. securityhandlers
provide a convenience, whereby the request, declared scopes, and the security schema itself are provided as parameters to each securityhandlers
callback that you define. The code you write in each callback can then perform authentication and authorization checks. Note that the same can be achieved using standard Express middleware. The difference is that securityhandlers
provide you the OpenAPI schema data described in your specification_. Ulimately, this means, you don't have to duplicate that information in your code.All in all, security
handlers
are purely optional and are provided as a convenience.
Security handlers specify a set of custom security handlers to be used to validate security i.e. authentication and authorization. If a security handlers
object is specified, a handler must be defined for all securities. If security `handlers are not specified, a default handler is always used. The default handler will validate against the OpenAPI spec, then call the next middleware.
If security handlers
are specified, the validator will validate against the OpenAPI spec, then call the security handler providing it the Express request, the security scopes, and the security schema object.
- security
handlers
is an object that maps security keys to security handler functions. Each security key must correspond tosecurityScheme
name. ThevalidateSecurity.handlers
object signature is as follows:
{
validateSecurity: {
handlers: {
[securityKey]: function(
req: Express.Request,
scopes: string[],
schema: SecuritySchemeObject
): void,
}
}
}
For example:
validateSecurity: {
handlers: {
ApiKeyAuth: function(req, scopes, schema) {
console.log('apikey handler throws custom error', scopes, schema);
throw Error('my message');
},
}
}
The express-openapi-validator performs a basic validation pass prior to delegating to security handlers. If basic validation passes, security handler function(s) are invoked.
In order to signal an auth failure, the security handler function must either:
throw { status: 403, message: 'forbidden' }
throw Error('optional message')
return false
- return a promise which resolves to
false
e.gPromise.resolve(false)
- return a promise rejection e.g.
Promise.reject({ status: 401, message: 'yikes' });
Promise.reject(Error('optional 'message')
Promise.reject(false)
Note: error status 401
is returned, unless option i.
above is used
Some examples:
validateSecurity: {
handlers: {
ApiKeyAuth: (req, scopes, schema) => {
throw Error('my message');
},
OpenID: async (req, scopes, schema) => {
throw { status: 403, message: 'forbidden' }
},
BasicAuth: (req, scopes, schema) => {
return Promise.resolve(false);
},
...
}
}
In order to grant authz, the handler function must either:
return true
- return a promise which resolves to
true
Some examples
validateSecurity: {
handlers: {
ApiKeyAuth: (req, scopes, schema) => {
return true;
},
BearerAuth: async (req, scopes, schema) => {
return true;
},
...
}
}
Each security handlers
' securityKey
must match a components/securitySchemes
property
components:
securitySchemes:
ApiKeyAuth: # <-- Note this name must be used as the name handler function property
type: apiKey
in: header
name: X-API-Key
See OpenAPI 3 authentication for securityScheme
and security
documentation See examples from unit tests
Example: Multiple Validators and API specs
It may be useful to serve multiple APIs with separate specs via single service. An example might be an API that serves both v1
and v2
from the same service. The sample code below shows how one might accomplish this.
See complete example
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const http = require('http');
const OpenApiValidator = require('express-openapi-validator');
app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.text());
app.use(bodyParser.json());
const versions = [1, 2];
for (const v of versions) {
const apiSpec = path.join(__dirname, `api.v${v}.yaml`);
app.use(
OpenApiValidator.middleware({
apiSpec,
}),
);
routes(app, v);
}
http.createServer(app).listen(3000);
console.log('Listening on port 3000');
function routes(app, v) {
if (v === 1) routesV1(app);
if (v === 2) routesV2(app);
}
function routesV1(app) {
const v = '/v1';
app.post(`${v}/pets`, (req, res, next) => {
res.json({ ...req.body });
});
app.get(`${v}/pets`, (req, res, next) => {
res.json([
{
id: 1,
name: 'happy',
type: 'cat',
},
]);
});
app.use((err, req, res, next) => {
// format error
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});
}
function routesV2(app) {
const v = '/v2';
app.get(`${v}/pets`, (req, res, next) => {
res.json([
{
pet_id: 1,
pet_name: 'happy',
pet_type: 'kitty',
},
]);
});
app.post(`${v}/pets`, (req, res, next) => {
res.json({ ...req.body });
});
app.use((err, req, res, next) => {
// format error
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});
}
module.exports = app;
FAQ
Q: How do I match paths, like those described in RFC-6570?
A: OpenAPI 3.0 does not support RFC-6570. That said, we provide a minimalistic mechanism that conforms syntactically to OpenAPI 3 and accomplishes a common use case. For example, matching file paths and storing the matched path in req.params
Using the following OpenAPI 3.x defintion
/files/{path}*:
get:
parameters:
- name: path
in: path
required: true
schema:
type: string
With the following Express route defintion
app.get(`/files/:path(*)`, (req, res) => { /* do stuff */ }`
A path like /files/some/long/path
will pass validation. The Express req.params.path
property will hold the value some/long/path
.
Q: Can I use discriminators with oneOf
and anyOf
?
A: Currently, there is support for top level discriminators. See top-level discriminator example
Q: What happened to the securityHandlers
property?
A: In v3, securityHandlers
have been replaced by validateSecurity.handlers
. To use v3 security handlers, move your existing security handlers to the new property. No other change is required. Note that the v2 securityHandlers
property is supported in v3, but deprecated
Q: What happened to the multerOpts
property?
A: In v3, multerOpts
have been replaced by fileUploader
. In order to use the v3 fileUploader
, move your multer options to fileUploader
No other change is required. Note that the v2 multerOpts
property is supported in v3, but deprecated
Q: I can disallow unknown query parameters with allowUnknownQueryParameters: false
. How can disallow unknown body parameters?
A: Add additionalProperties: false
when describing e.g a requestBody
to ensure that additional properties are not allowed. For example:
Pet:
additionalProperties: false
required:
- name
properties:
name:
type: string
type:
type: string
Q: I upgraded from from v2 to v3 and validation no longer works. How do I fix it?
A: In version 2.x.x, the install
method was executed synchronously, in 3.x it's executed asynchronously. To get v2 behavior in v3, use the installSync
method. See the synchronous section for details.
Q: Can I use express-openapi-validator
with swagger-ui-express
?
A: Yes. Be sure to use
the swagger-ui-express
serve middleware prior to installing OpenApiValidator
. This will ensure that swagger-ui-express
is able to fully prepare the spec before before OpenApiValidator attempts to use it. For example:
const swaggerUi = require('swagger-ui-express')
const OpenApiValidator = require('express-openapi-validator')
...
app.use('/', swaggerUi.serve, swaggerUi.setup(documentation))
app.use(OpenApiValidator.middleware({
apiSpec, // api spec JSON object
//... other options
}
}))
Q: I have a handler function defined on an express.Router
. If i call req.params
each param value has type string
. If i define same handler function on an express.Application
, each value in req.params
is already coerced to the type declare in my spec. Why not coerce theseF values on an express.Router
?
A: First, it's important to note that this behavior does not impact validation. The validator will validate against the type defined in your spec.
In order to modify the req.params
, express requires that a param handler be registered e.g. app.param(...)
or router.param(...)
. Since app
is available to middleware functions, the validator registers an app.param
handler to coerce and modify the values of req.params
to their declared types. Unfortunately, express does not provide a means to determine the current router from a middleware function, hence the validator is unable to register the same param handler on an express router. Ultimately, this means if your handler function is defined on app
, the values of req.params
will be coerced to their declared types. If your handler function is declare on an express.Router
, the values of req.params
values will be of type string
(You must coerce them e.g. parseInt(req.params.id)
).
Contributors ✨
Contributions welcome! Here's how to contribute.
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome!
Community articles, blogs, and tutorials
Seeking content creators…
Have you written an article, blog, or tutorial that uses express-openapi-validator
?
Please post your links here
We plan to publicize a variety of links here.