swagger-routes - 路由模块

github地址

swagger-routes 是使用 Swagger 2.0(OpenAPI) 规范生成和注册 RestifyExpress 路由处理程序的工具。

要求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成功。

例如,如果CatalogPlaybackscope存在,下面的定义将通过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解析,包括端口等。

如果这还不够,你还有两个选择:

  1. 启动服务时设置API_HOST的环境变量,SwaggerRoutes将会获取此设置的值。

  2. 在调用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)

  1. 授权器中间件(authorizer middleware)如果在路由上有安全限制,则每个路由的授权器将需要验证附加到请求的权限。

  2. 自定义中间件(custom middleware),如果路由定义一个或多个中间件,这些将按顺序执行。

  3. 校验中间件(validation middleware) ,传入的请求现在将针对给定操作的Swagger规范进行验证。

  4. 业务处理程序(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 method

  • consumes: Populated with the top levelconsumesunless the operation defines its own.

  • produces: Populated with the top levelproducesunless 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?