文章目录

  • Api的迷思
  • Swagger
    • Swagger概述
    • 在Sails中集成Swagger
      • 安装
      • Swagger 生成设置
      • 生成的内容
      • SwaggerUI
  • Assets和.tmp
  • Blueprint 蓝图
    • Blueprint是什么
    • Blueprint 配置
    • local.js
  • 进一步控制Swagger输出
    • 路由过滤
    • 路由的Swagger配置
    • 进一步优化
    • Authorization

Api的迷思

我们都知道写代码要写注释是常识。而写Api程序注释是不够的,因为使用你Api的人看不到源代码。为了让使用你Api的人可以调用,我们还必须为每一个Api写调用说明文档,甚至还必须给出使用示例。这为写Api的程序员多了不少的工作量,并且许多程序员写代码会比较认真,写文档会因为被认为是不那么重要的工作比较马虎,这也导致文档质量参差不齐。另外Api是会变的,每一次变动还需要重新修改使用说明文档,这也会照成错漏,导致文档和实际情况不一致。文档一大堆,时间一长也不知道文档都放哪里了。一个项目半年之后出现找不到说明文档的情况非常正常。如果项目后面有人事调动,新人还需要和旧人进行文档方面的交接。。。文档是许多程序员的痛,在行业内一直都没有很好的解决,直到出现了Swagger。

Swagger

Swagger概述

Swagger是一套基于OpenAPI规范构建的开源工具,它是一种规范,你只需要按照它的规范去定义接口及接口相关的信息。再通过Swagger衍生出来的一系列项目和工具,就可以做到生成各种格式的接口文档,以及在线接口调试页面等等。在开发新版本或者迭代版本的时候,只需要更新Swagger描述文件,就可以再次自动生成接口文档,做到调用端代码、服务端代码以及接口文档的一致性。
为了能更感性直观的理解Swagger,请大家前往https://editor.swagger.io/

归纳一下,大约有以下几点

  • 一定格式的Json来描述你的Api
  • 自动根据你的Json生成html页面(SwaggerUI)
  • 在生成的SwaggerUI里面有分组功能,有概述,描述,参数,数据示例,数据结构,试一试(Try it out)功能

如果你是前端程序员,看到这样的文档是不会也会感觉心里更有底了。( ̄▽ ̄)~*

在Sails中集成Swagger

使用Swagger看起来很美好,但是如果Json还是需要自己来写,那就是另外一种痛苦了。有许多聪明的懒人也已经注意到这个,各种各样的可以自动生成Swagger Json的工具应运而生,并且其中还有专门为Sails设计的开源库。Let’s go!

安装

npm install sails-hook-swagger-generator --save

安装完这个库之后,还有一个动作一定不能忽略,就是要在根目录下创建一个swagger文件夹,因为该库默认生成的Json是放在这个文件夹的,如果没有这个文件夹,生成的时候就会产生错误。如果已经做好了准备,启动我们的系统吧,运行:

node app.js

Boom! 在swagger里面多出了一个名为swagger.json的文件,如果把swagger.json的内容copy到https://editor.swagger.io/ 网站上左边的编辑器里面,替代原来的内容。我们将会在右边看到我们自己的Api文档。(大家可以动手试试)

Swagger 生成设置

虽然我们可以看到生成的api文档了,但是怎么生成的我们还是需要进行配置的。在config文件夹里面添加swaggergenerator.js,然后在config/swaggergenerator.js写类似如下代码:

module.exports['swagger-generator'] = {disabled: false,//如果设置为true,就表示不生成,这个选项是我们开发完成投入生成的时候需要的swaggerJsonPath: './swagger/swagger.json',//这是生成json文件的位置,后面我可以进行修改swagger: {openapi: '3.0.0',info: {title: 'Api文档',description: 'AdminSystem 后端Api说明及测试用例',termsOfService: 'https://github.com/orgs/PassionOrganization/teams/codeteam',contact: {name: 'JimmyTsai', url: 'https://github.com/JimmyTsai75', email: 'zzcaism@163.com'},license: false,//有不需要显示的选项,可以设置为falseversion: '1.0.0'},servers: [{ url: 'http://localhost:1898/' }],externalDocs: {description:'点击查看更详细说明',url: "https://passionorganization.gitbook.io/sailsjs-cong-ling-kai-shi/"},},defaults: {responses: {'200': { description: 'The requested resource' },'404': { description: 'Resource not found' },'500': { description: 'Internal server error' }}},    includeRoute: function(routeInfo) { return true; },//这个是过滤函数,可以根据需要设置updateBlueprintActionTemplates: false,//可以通过这个函数设置sails蓝图模版postProcess: function(specifications) {  }//可以在函数中拦截输出,并修改输出内容
};

上面的这段代码是对Swagger generator进行配置,具体用请看注释。

生成的内容

根据配置生成的内容是swagger.json,这是个约定格式文档,大家可以打开生成的内容看看,这个地方还需要解释一下的有三个地方:

  • tags:标签是对api进行分组的一种可折叠的html页面组件,我们可以设置要生成的api放在哪里标签从而实现分组
  • components:这个里面包含两个项目,schemas和parameters 分别对应关联的查询参数和数据模型model,还有一个安全项后面的Authorization会详述。
  • paths:这个就是api路径,就是我们写在config/routes.js里面的路由路径,展开之后可以看到里面的具体设置,包括指定标签,查询参数,响应说明等

以上三个主要组件分别对应页面上的内容如下图所示:

这些看起来有点复杂,大致了解一下就可以,后面的具体操作可以让我们逐渐的理解更多细节,多做几个就完全明白了。

SwaggerUI

到目前为止,我们已经可以自动产生api的描述文档swagger.json,并且如果我们把内容粘贴到https://editor.swagger.io/ 这个网站上面,我们还可以看到展示效果,但是这依然不够,我们需要在我们自己的后端上面就能够展示。那我们就需要把整个https://editor.swagger.io/ 网站搬到我们的sails后端系统上,幸运的是SwaggerUI是开源的,我们可以直接下载下来

  • 下载SwaggerUI :这个开源库的网址如下:https://github.com/swagger-api/swagger-ui 我们不打算修改他,只需要download它的zip文档就够了

  • Copy:下载并解压之后,源代码根目录下面有一个dist文件夹,我们只需要把这个文件夹复制到我们sails项目根目录下assets里面就可以了。现在我们已经拥有了SwaggerUI,在浏览器中输入:http://localhost:1898/dist/#/,我们可以看到如下:

  • 快好了,我们还差一步,因为现在SwaggerUI不知道我们的swagger.json文档在哪里。打开assets/dist/swagger-initializer.js
    修改SwaggerUIBundle.url指向网站根目录…/swagger.json,代码如下:

//assets/dist/swagger-initializer.js
window.onload = function() {  window.ui = SwaggerUIBundle({url: "../swagger.json",//**修改这个地方为上一级目录的swagger.json**dom_id: '#swagger-ui',deepLinking: true,presets: [SwaggerUIBundle.presets.apis,SwaggerUIStandalonePreset],plugins: [SwaggerUIBundle.plugins.DownloadUrl],layout: "StandaloneLayout"});//</editor-fold>
};
  • 修改后,我们还需要把我们生成的swagger.json复制到assets目录下面,操作后我们的文件目录大约是这样的:
 |-- assets|   |-- dist|   |-- favicon.ico|   |-- dependencies|   |   |-- .gitkeep|   |   |-- sails.io.js|   |-- images|   |-- swagger.json  //这个就是我们复制过来的.......

记得保存一下assets/dist/swagger-initializer.js,刷新浏览器应该就看到了

Assets和.tmp

  • 为什么拷贝dist文件夹到assets目录下,在浏览器的http://localhost:1898/dist/下面就可以看到swaggerUI这个网页?
    那是因为sails约定assets文件夹为网站静态资源文件夹,sails启动的时候会运行服务程序,并且把assets文件夹里面的内容copy到.tmp/public,sails服务以.tmp/public文件夹为web服务的根目录,所以我们复制过去的swaggerUI可以运行起来。并且我们如果打开.tmp文件夹,我们可以看到,我们复制到assets文件夹里面的swagger.json会自动复制到.tmp/public里面。
  • 我们可以做得更好一点,既然我们生成的swagger.json要复制到assets文件夹里面,我们不应该每次都手动操作,我们完全可以修改swagger.json的生成路径,让它直接生成在assets文件夹里面就好了。
module.exports['swagger-generator'] = {disabled: false,swaggerJsonPath: 'assets/swagger.json',//这个地方直接改成我们希望的目的地....

有时候我们修改swagger的生成规则(config/swaggergenerator.js)之后,保存,重启服务并且刷新浏览器,swaggerUI里面的内容没有及时变成我们要的,这个时候可以到.tmp/public 里面把swagger.json删除掉再重试

再重复一下,现代软件工程有一个非常好的理念叫“约定高于配置”(convention over configuration),在软件工程里面这是一种非常简约的设计思想,sails许多地方都体现了这点。

这种复制是通过grunt这个库来实现的,如果需要修改,可以通过tasks/config/copy.js来实现。

另外dist文件夹名称和我们的功能不是很搭,建议修改文件夹名称为apiDoc

Blueprint 蓝图

如果按照上面的操作成功了,我们应该是可以看到许多我们的Api说明的,但是很奇怪的是,多出来许多我们没有写的路由,如下图:

我的config/routes.js里面是这样的:里面没有’GET /user’,‘PATCH /user/{id}’ 等等

POST /api/login/account': { action: 'users/check' },'GET /api/currentUser': { action: 'users/curUser' },'GET /api/notices': { action: 'users/notices' },'POST /api/login/outLogin': { action: 'users/logout' },'GET /api/crudDemo': { action: 'Amis/crudDemo' },'post /api/crudnew': { action: 'Amis/crudNew' },'delete /api/crudDelete': { action: 'Amis/crudDelete' },
....

但是swagger generator 帮我们找出来了,这是怎么回事?

Blueprint是什么

在sails里面只要创建了model,blueprint 实际上已经帮你实现了表的增删改查了,根本不需要写任何增删改查的代码,也不需要在config/routes.js里面添加任意的控制器,这些代码已经做好。我们以我们做好的user数据模型为例,我们在config/routes.js里面添加的路由都是以api开头的,但是如果我们打开postman软件,输入http://localhost:1898/user,我们会发现,Wow,竟然是可以查询到所有用户数据的:

Sails 旨在减少代码量以及启动和运行功能性应用程序所需的时间。 蓝图是 Sails根据您的应用程序设计快速生成 API路由和操作的方式。默认情况下,它会自动完成Find,Create,Update,Destroy,Add几个操作。

Blueprint 配置

Blueprint API是一个很sweet的设计,但是我不是很建议使用,有几个原因

  • 采用影子路由的方式,不熟悉sails之blueprint的维护人员在config/routes.js里面看不到,会比较困惑。
  • 多个影子路由默认采用Get,这是很容易给CSRF(Cross-site request forgery) 提供方便(当然可以通过一些安全手段解决但是没有必要留这个口)
  • 有一些查询是需要权限控制和身份认证的,如果要使用blueprint,我们依然需要对它进行设置,那还不如我们自己写
  • 不少数据库的CRUD是需要定制的,这样我们还是需要对blueprint进行覆盖

所以,如果我们要设计一个安全性要求不高的,我们可以直接采用blueprint或是混合开发,而我们对系统安全有一定要求的,那我们还是要控制blueprint的使用,甚至是关闭它。
如果需要对blueprint进行配置,我们可以进入config/blueprints.js里面进行设置。我们试着把它的所有功能关闭掉,可以这样设置:

//config/blueprints.js
module.exports.blueprints = {actions: false,rest: false,shortcuts: false,autoWatch:false,
};

以上配置更加具体的说明见:https://sailsjs.com/documentation/concepts/blueprints

配置后大家可以在postman里面再次测试(需要保存后重新启动服务),如果成功,应该可以看到postman里面显示Not Found,并且我们的swaggerUI里面多出来的路由也没有了。

swaggerUI 里面的自动变化前提是你有修改swagger.json的生成路径(swaggerJsonPath)

local.js

关闭blueprint的操作会受到sails的Environment(环境)变量设置的影响。Env是Nodejs程序开发经常使用的做法。目的是为了实现开发周期内(development)和开发完成的运营周期(production)可以有不同的配置,比如是否跨域,stockets配置等。在sails里面,开发环境可以通过启动参数或是config/local.js来配置,这个文件同时也可以配置端口号等其它比较敏感的内容,所以一般是会在gitignore里面设置为不上传到git服务器的。(我们如果是私有库并且可信任,这个文件也是可以上传的)。
如果我们在local.js里面把环境变量设置为production如下:

//config/local.js
module.exports = {port:1898,environment: 'production'
};

这个时候,sails会加载config/env/production.js并且覆盖config里面的设置,这种情况,我们要关闭blueprint api的话,也需要同时修改config/env/production.js对应设置。

sails启动后的画面是有显示当前环境配置的

进一步控制Swagger输出

路由过滤

就算屏蔽掉了blueprint的“影子路由”我们也依然有需要对其它路由进行过来,避免我们的Api文档多维护没有使用到的路由,那我们就需要使用路由过滤函数。这个函数在config/swaggergenerator.js

//config/swaggergenerator.js
module.exports['swagger-generator'] = {disabled: false,swaggerJsonPath: 'assets/swagger.json',...includeRoute: (routeInfo) => {if (routeInfo.isShortcutBlueprintRoute == true) return false;if (routeInfo.verb == 'put' || routeInfo.verb == 'patch') return false;if (routeInfo.action == 'security/grant-csrf-token') return false;if (!routeInfo.path.startsWith('/api/')) return false;    return true;},

比如我们通过上面的过滤函数,过滤掉所有不是api开头的路由
另外,我们还可以对一些默认的response进行配置:

defaults: {responses: {'400': { description: '验证错误' },'403': { description: '没有权限访问' },'401': { description: '认证失败' },'200': { description: '请求成功' },'404': { description: '找不到请求,请检查url和method是否正确' },'500': { description: '服务器内部错误' }},    },

路由的Swagger配置

目前,我们看到的一些路由是类似这样的:

尽管里面许多信息还是空白的,但是try it out功能按钮是没有问题的,并且也真的可以做一些类似postman的测试了(有了Swagger,并不表示就不用postman了,它有它的不可替代),当然我们还需要进一步把Api里面具体Action的详细使用描述得更加清楚一些的。这些我们可以直接在swagger.json里面手动填写,但这不是个好主意,因为我们的Api在开发过程是不断变化的,某个action一开始时实现的功能和最终实现的功能可能相差十万八千里。
我们需要像写代码注释一样的方式,把这些描述就写在代码里面,如果有变动也应该在源代码里面变动,这样我们日后就可以和管理源代码一起管理这些Api文档了。甚至它们还应该一起被push到我们的git仓库里面去的。
有两个选项,一个是写在每个controller.ts源代码里面,一个是写在config/routes.js 我更倾向于后者因为它更集中,管理起来更加方便一些。

  • 打开config/routes.js,把原来的’POST /api/userCreate’: {action: ‘User/create’} 修改成如下代码:
'POST /api/userCreate': {swagger: {summary: '创建用户',description: `包含email,password等信息;post body里面包含email,其中email格式由前端验证,后端只负责检查是否非空。email字段具备唯一性,添加的email如果有重复系统将返回500错误。`,tags: ['用户管理'],//放到用户管理这个tag标签里面//post的时候body里面的内容requestBody: {content: {'application/json': {schema: {properties: {email: { type: 'string' },password: { type: 'string' },nickname: { type: 'string' }},required: ['email', 'password'],}}}},//参数parameters:[{in:'header', name:'justTest',required:false,schema:{type:'string',example:'what '}}],//对响应的400代码做解析responses: {'400': { description: '验证错误' },},//关闭扩展文档显示externalDocs: false,},// action不变action: 'User/create'},
  • 打开http://localhost:1898/apidoc/ (dist文件夹的名称已经改成apiDoc)可以见到如下展示效果

进一步优化

还是有一点小小的不舒服:

  • 代码太长:config/routes.js这个文件变得太长了,一个路由就需要30多行,路由多起来后代码会变成很长,不利于日后的维护。通常我们希望一个js文件长度尽量控制在500-1000行,如果我们把每个表的增删改查都做成路由,10个table就需要1200行了,最好能够把swagger独立出来,减少routes.js的代码长度。
  • swagger不支持typescript:通常并且我们再routes.js里面写swagger没有任何的类型提示和限制,如果可以优化成typescript就更好了。

做法也不难:
一、在根目录下创建swagger,增加UserSwagger.ts文件,import sails-hook-swagger-generator里面对应的interfaces,把原本写在routes.js里面的swagger相关代码移过来:

//swagger/UserSwagger.ts
//导入类型支持
import { SwaggerActionAttribute } from "sails-hook-swagger-generator/lib/interfaces"
//用户控制器所在Tag
const Tag1 = "用户管理";
//移植原来的代码
const userCreate:SwaggerActionAttribute={summary: '创建用户',description: `包含email,password等信息;post body里面包含email,其中email格式由前端验证,后端只负责检查是否非空。email字段具备唯一性,添加的email如果有重复系统将返回500错误。`,tags: [Tag1],requestBody: {content: {'application/json': {schema: {properties: {email: { type: 'string' },password: { type: 'string' },nickname: { type: 'string' }},required: ['email', 'password'],}}}},parameters:[{in:'header', name:'justTest',required:false,schema:{type:'string',example:'what '}}],responses: {'400': { description: '验证错误' },},externalDocs: false,
};
export { userCreate};

二、修改config/routes.js

import { userCreaten } from 'swagger/UserSwagger';
'POST /api/userCreate': {swagger: userCreate,action: 'User/create'},

三、增加别名
在package.json里面增加路径别名如下:

  "_moduleAliases": {"utils": "./utils","swagger":"./swagger"},

cool,现在的代码看起来清爽多了。

因为routes.js里面我们采用’swagger/UserSwagger’,如果没有别名,这个地方要改成import { userCreaten } from ‘…/swagger/UserSwagger’;但是这种相对路径的写法容易错,采用路径别名会舒服一些。路径别名请参考本系列文章之《二、在Sails中使用Typescript》

Authorization

还剩最后一个问题了,我们上一篇讲的Jwt认证之后,我们有一些控制器的动作是需要携带token的,遇到这种情况,Swagger上面的try it out功能就会受挫,因为我们没有可以让测试人员输入token的地方。

swagger.json上面是可以有安全选项的,在components这个节点里面,除了我们前面说的schemas和parameters之外,还可以添加一个securitySchemes项。具体的做法是在swagger.json创建之前做一个拦截函数,幸运的是config/swaggergenerator.js里面是有提供这样的hook的,我们通过修改postProcess函数就可以实现,做法和路由拦截类似:

//config/swaggergenerator.js
module.exports['swagger-generator'] = {....postProcess: function (specifications) {let sch = specifications.components;//在Top-Level Swagger Defintions中添加身份认证项Authorizesch.securitySchemes = {APIKeyHeader: {type: "apiKey",in: "header",name: "Authorization",description: "Bearer xxxx"}}//在Top-Level Swagger 中添加安全选项,如果在顶级属性中添加安全项,则每一个path上都可以执行Authorize//如果此处没有添加,可以在每个action中的swagger里面添加security,有添加的path才能进行身份认证//大小写敏感specifications.security = [{APIKeyHeader: []}];},.....
}

在将最终生成的Swagger写入swagger.json之前,可以使用postProcess函数进行拦截

注意APIKeyHeader 这个名称,在swagger.json里面是大小写敏感的,这是一个小坑。

成功之后,刷新swaggerUI,可见如下:可以在try it out 的之前,先做身份认证:在弹窗的value里面输入Bearer xxxx… (登录后获取的token),并且我们可看到每个path后面都带一个锁的图标,如果需要修改token,可以点击任意一个path上lock图标实现再次输入token

可以尝试在api/userRetrieve这个path上面使用try it out 功能,没有Authorization的时候会显示“您没有权限访问本页面",Authorizations之后就有可以正常查询了

四、Sails项目的Api文档——集成Swagger解决方案相关推荐

  1. Maven学习总结(43)——利用javadoc插件生成项目的API文档

    在进行Java学习的时候,相信大家都看过在线或者下载的java api文档,可能是html格式或者chm格式的,其实这些参考文档也是很容易生成的,这里介绍一个maven的插件来实现项目代码文档的生成. ...

  2. SpringCloud微服务项目的api文档聚合

    目录 原理简介 在网关中配置好路由 Spring Cloud Gatway + Springfox 第一步.在各项目里配置Springfox 第二步.在网关中编写api文档资源路径 注意 Spring ...

  3. 如何生成项目的chm文档

    如何生成项目的chm文档 2014-11-30 Generate .chm based documentation of your project using SandCastle  tool 转载于 ...

  4. knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案

    knife4j knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui,取名kni4j是希望它能像一把匕首一样小巧,轻量, ...

  5. API文档工具-Swagger的集成

    最近安装了API文档工具swagger,因为Github上已有详细安装教程,且安装过程中没有碰到大的阻碍,所以此文仅对这次安装做一份大致记录 相关网站 Swagger 官方地址: http://swa ...

  6. Java:一个API文档框架Swagger

    新建SpringBoot项目,添加依赖 <!-- Swagger2 --> <dependency><groupId>io.springfox</groupI ...

  7. SpringBoot工作日记(4)API文档框架 —— Swagger

    在使用了SpringBoot之后,我们不再像SpringMVC那样使用JSP了,通常使用Controller做接口,然后前端页面使用Ajax访问接口来交互数据,将前后端进行分离.这样约定接口格式就成了 ...

  8. idea的springboot项目的xml文档中查询语句有黄色背景

    接下来介绍如何不显示黄色背景的方法: 1.File -- Settings (或者Ctrl + Alt + S 快捷键)打开设置界面: 2.然后找到 Editor -- Inspections : 3 ...

  9. Eolink 征文活动- -后端研发需要的API文档工具

    Eolink功能太多,一两篇文章聊不完,这篇文章只是聊聊Eolink的API文档管理功能.     首先大致说说我所认知的API文档工具历史. 我所知的API文档工具历史     我是2010年左右参 ...

  10. Spring Boot 集成 Swagger 生成 RESTful API 文档

    原文链接: Spring Boot 集成 Swagger 生成 RESTful API 文档 简介 Swagger 官网是这么描述它的:The Best APIs are Built with Swa ...

最新文章

  1. mysql金库模式_Python vault-anyconfig包_程序模块 - PyPI - Python中文网
  2. 人工智能/云原生/数据科学/计算等方向内容整理志愿者招募了!
  3. Bishops Alliance—— 最大上升子序列
  4. 活动事务日志以及事务的类型
  5. 压缩pdf大小_PDF压缩到指定大小该怎么操作?分享完成PDF压缩超好用的方法
  6. H5中CSS3动画的性能优化
  7. SCOM 2016 配置报警邮件 (下)
  8. MQTT协议(1)-简介
  9. 科学计算机功能键介绍ndf,DNF:非常实用的五个小技巧,你不会还不知道吧!-dnf快捷键设置...
  10. 阿里云服务器SSL不可用
  11. Snakemake报错Outputs of incorrect type
  12. SpringBoot并发压测工具ContiPerf
  13. H5页面跳转小程序分享-山东标梵互动
  14. 编程输出1000以内所有的完数,并输出其所有因子。所谓完数,即一个数的所有因子(除其自身)之和恰好等于其自身。如6就是一个完数,6=1+2+3。**输出格式要求:“\n%5d\n“, “%5d“
  15. 斗鱼html5插件不可用,斗鱼html5插件
  16. “滴灌”代替“漫灌”:“全链路增长”的百度联盟解
  17. 《软件工程(第4版?修订版)》目录—导读
  18. oracle 设置监听和服务,oracle服务端和客户端之间的网络监听如何设置呢?
  19. 想做产品经理,首先应该掌握哪些技能
  20. 奋斗吧,程序员——第二十八章 叹年来踪迹,何事苦淹留

热门文章

  1. 30天自制操作系统-Hello OS
  2. 什么是云主机,云主机是什么样的?
  3. Bithumb Global AMA丨Cred加速实现开放金融-打造区块链上蚂蚁金服
  4. Android系统java/native crash和anr异常处理流程以及DroboxManagerService日志记录
  5. UI 移动端设计尺寸
  6. 2018 Android 文字转语音(中文) TextToSpeech+科大讯飞语音引擎3.0
  7. meta http-equiv 属性 详解
  8. H指数(h-index)的Python实现
  9. (P46)面向对象版表达式计算器:让表达式计算器支持变量赋值 ,Calc类实现
  10. 交换机VLANIF配置