swagger-routes - 路由模块
swagger-routes 是使用 Swagger 2.0(OpenAPI) 规范生成和注册 Restify 或 Express 路由处理程序的工具。
要求node v4.0+
Express
const swaggerRoutes = require('swagger-routes')
const express = require('express')
const app = express()
swaggerRoutes(app, {
api: './api.yml',
handlers: './src/handlers',
authorizers: './src/handlers/security'
})
app.listen(8080)
Restify
const swaggerRoutes = require('swagger-routes')
const restify = require('restify')
const server = restify.createServer()
swaggerRoutes(server, {
api: './api.yml',
handlers: './src/handlers',
authorizers: './src/handlers/security'
})
server.listen(8080)
Options
api
: 你的openapi规范的地址docsPath
: openapi的url访问路径,默认是:/api-docs
.docsMiddleware
: 可选的中间件函数,可以确保api docs 节点 安全性。handlers
: 写业务的处理程序文件所在的目录。默认目录是./handlers。
也可以是一个返回操作函数。authorizers
: 授权文件目录。 默认目录是:./security。
可选地,它可以是一个返回授权中间件的函数,给出了一个swagger的安全方案。.maintainHeaders
: 将生成的处理程序文档头与swagger API保持同步。默认为false。
操作处理文件(Operation Handlers)
具体写业务逻辑的地方
您可以选择为每个Swagger操作定义和维护处理程序文件(Handler Files),或者提供工厂函数,该工厂函数创建给定操作的处理程序函数。
Handler Files
如果每个处理程序需要自己唯一的逻辑来处理操作请求,使用单独的处理程序文件是一个不错的选择。
处理程序文件必须在处理它的Swagger操作之后命名,例如.listPets.js
。
除非启用了group选项,否则所有处理程序文件都必须在同一个目录中,在这种情况下,处理程序文件应该位于其主标记名的文件夹下(参见下面的生成处理程序文件)。
File Contents
应该导出一个称为"handler"
的函数来处理传入的操作请求。
exports.handler = function listPets(req, res, next) {
}
您还可以导出要在处理程序之前执行的中间件函数。
exports.middleware = preprocess
function preprocess(req, res, next) {
next()
}
中间件可以是有序列表。
exports.middleware = [
function preprocess1(req, res, next) { next() },
function preprocess2(req, res, next) { next() }
]
生成处理文件
为了节省一些时间,有一个捆绑的工具可以基于Swagger openapi定义中的操作以及Mustache模板生成处理程序文件。
这个工具在默认情况下是打开的,所以在第一次运行swaggerRoutes
时检查处理程序文件夹,并且根据swagger api 文档中定义的operationId
来命名。
每次启动应用程序时,swaggerRoutes
将查看是否缺少任何操作处理程序,并为其中任何操作处理程序生成存根处理程序。如果存在一个处理文件,它将不会做什么操作,也就是说,这是非破坏性的,因此您可以自由编辑它们不用担心会被再次生成覆盖。
注意,如果打开syncHeaders
选项,则处理程序文件的头部将根据Swagger api在每次运行时更新。这使处理程序文档保持最新,因此您可以很容易地看到对于给定操作的请求伴随哪些参数。它将覆盖任何对标题的编辑,因此如果您不打算手动编辑它们,则打开这个选项即可。
当重新运行发现处理程序不再使用时,它们将被加上一个“_”的前缀,因此listPets.js
将成为_listPets.js
。这可以帮您识别不再使用的处理程序,根据自己的愿意,可以删除/重命名它们。
如果稍后在您的规范中重新启用处理程序并重新运行,则下划线将被删除。
请注意,只有在group选项false不开启时才支持前缀删除处理程序的这一特性。
默认模板在此定义,但您可以通过扩展处理程序选项来提供自己的模板。
{
...
handlers: {
path: './src/handlers',
template: './template/handler.mustache', // can also be set with a loaded template
getTemplateView: operation => operation, // define the object to be rendered by your template
create: operation => (req, res) => {}, // see Handler Factory section for details
generate: true, // hander file generation on by default
group: false // when true each handler file will be placed under a directory named after its primary tag
}
}
Handler Factory
如果处理程序非常相似,例如:将他们的请求处理委托给服务类。那么工厂函数是更好的选项。
创建处理器工厂(Creating a Handler Factory)
在注册路由时,可以将handlers
定义为函数。它接收一个Swagger的操作,并返回负责处理它的请求处理程序。
const swaggerRoutes = require('swagger-routes')
swaggerRoutes(app, {
api: './api.yml',
handlers: createHandler
})
function createHandler(operation) {
return function handler(req, res, next) {
res.send(operation.id)
}
}
如果返回一个处理程序函数,那么它将优先于同一操作的处理程序文件。
路由中间件(Route Middleware)
正如文件处理程序,同样的可以定义路由中间件,在createHandler
时:
function createHandler(operation) {
return {
middleware: function preprocess(req, res, next) { next() },
handler: function handler(req, res, next) { res.send(operation.id) }
}
}
像之前说的一样,路由中间件可以是有序列表。
function createHandler(operation) {
return {
middleware: [
function preprocess1(req, res, next) { next() },
function preprocess2(req, res, next) { next() }
],
handler: function handler(req, res, next) { res.send(operation.id) }
}
}
授权 (Authorizers)
当Swagger api指定一个或多个安全方案(security schemes)时,授权中间件可以保护选择进入一个或多个这些方案的路由。
就像处理程序一样,您可以在文件或工厂函数中定义授权程序。
File Authorizer
该授权文件应该在受保护的handle文件之后命名(例如petstore_auth.js
),并存在在authorizers
选项定义的目录路径中。它应该导出一个单一的中间件函数来授权请求。
module.exports = function petstore_auth(req, res, next) {
const token = decodeToken(req.headers.authorization)
if (token) {
const scopes = getTokenScopes(token)
next(req.verifyScopes(scopes))
} else {
const error = new Error('Unauthorized')
error.status = error.statusCode = 401
next(error)
}
}
以上就是一个例子。
正如您所看到的,如果安全方案是OAuth2,则提供一个verifyScopes
函数给 req。它获取从已验证的请求解码的scope数组,并验证是否存在为方案定义的所需scope(s)。如果它们不是,则返回403禁止访问的错误。
在为端点的安全性定义多个oauth scopes时,Swagger 希望所有这些作用域都存在,以便继续调用。作为这一扩展,swagger-routes
还支牌逻辑或令牌 scopes,因此如果存在,则verifyScopes
成功。
例如,如果Catalog
或Playback
scope存在,下面的定义将通过AUTH检查。
...
security:
- accountAuth:
- Catalog
- Playback
x-security:
accountAuth:
OR_scopes: true
如果没有提供验证的凭证 ,应该返回401 Unauthorized
。
生成授权文件 (Generating Authorizer Files)
非常像处理程序文件,授权文件也将为您生成和管理。
默认模板在此定义,但您可以通过扩展`authorizers
选项来提供自己的模板。
{
...
authorizers: {
path: './src/handlers/security',
template: './template/authorizer.mustache', // can also be set with a loaded template
getTemplateView: operation => operation, // define the object to be rendered by your template
create: operation => (req, res) => {}, // see Authorizer Factory section for details
generate: true // authorizer file generation on by default
}
}
Authorizer Factory
const swaggerRoutes = require('swagger-routes')
swaggerRoutes(app, {
api: './api.yml',
authorizers: createAuthorizer
})
function createAuthorizer(schemeId, securityScheme) {
return function authorizer(req, res, next) {
const token = decodeToken(req.headers.authorization)
if (token) {
const scopes = getTokenScopes(token)
next(req.verifyScopes(scopes))
} else {
const error = new Error('Invalid access token')
error.status = error.statusCode = 401
next(error)
}
}
}
请求验证(Request Validation)
每个传入的请求,将运行通过请求验证中间件才能到达具体的业务handle。这对请求执行JSON Schema验证,以确保它满足您定义的Swagger规范。如果不能满足此要求,将导致请求失败,并且不执行handle处理程序。
主机设置(Swagger Host)
如果你在不同的环境中(QA, Staging, Production)运行API,静态设置你的SiggigAPI的主机属性可能容易出错,这就是为什么我建议从你的代码中删除它的静态定义。默认情况下,这将从主机server解析,包括端口等。
如果这还不够,你还有两个选择:
启动服务时设置
API_HOST
的环境变量,SwaggerRoutes
将会获取此设置的值。在调用
swaggerRoutes
之后,在应用程序中手动设置app.swagger.host
。
const server = app.listen(3000, '0.0.0.0', () => {
app.swagger.host = `${server.address().address}:${server.address().port}`
})
路由堆栈执行顺序 (Route Stack Execution Order)
授权器中间件(authorizer middleware)如果在路由上有安全限制,则每个路由的授权器将需要验证附加到请求的权限。
自定义中间件(custom middleware),如果路由定义一个或多个中间件,这些将按顺序执行。
校验中间件(validation middleware) ,传入的请求现在将针对给定操作的Swagger规范进行验证。
业务处理程序(handler),假设所有以前的步骤通过,现在执行该处理程序。
高级用法
注册多个 Swagger Apis
您可能处于这样的情况下,您对API的每个主要版本都有一个Swagger的定义。如果是这种情况,并且希望在同一台服务器上处理不同的版本,那么您可以自由注册多个openapi 规范。
swaggerRoutes(server, {
api: './api-v1.yml',
handlers: './src/handlers/v1',
authorizers: './src/handlers/v1/security'
})
swaggerRoutes(server, {
api: './api-v2.yml',
handlers: './src/handlers/v2',
authorizers: './src/handlers/v2/security'
})
您需要确保每个路径之间没有冲突。最好的方法是给每个规范添加一个唯一的basePath,比如说/v1,v2等等。
操作对象
操作对象属性继承 Swagger spec 中定义的属性。 只有一些不同之处。
id
: 替换operationId
path
: 此操作的路由路径。method
: http methodconsumes
: Populated with the top levelconsumes
unless the operation defines its own.produces
: Populated with the top levelproduces
unless the operation defines its own.paramGroupSchemas
: 每个操作组的“JSON scheme ” ('header', 'path', 'query', 'body', 'formData')与操作相关。
致谢
这个类包的灵感主要来自于使用swaggerize-express的期间 。这是一个很棒的类包,你应该去看看。 我在编写一个新的替代实现后的推理是希望将所有路由操作ID而不是路径。这与Swagger客户端代码Gen的工作方式一致,使您更容易看到客户端SDK和服务器代码库作为一个整体。
考虑到它们之间的相似性,我还想在单个库中自动化编写并支持Restify和Express的大部分样板代码。
Last updated
Was this helpful?