通过本文,你可以学习到:
- egg中生成swagger-ui文档
创建项目
- 安装依赖:
$ mkdir egg-example && cd egg-example $ npm init egg --type=simple //使用官方的脚手架创建项目(simple:标准模板) $ npm i 复制代码
- 启动项目
$ npm run dev $ open http://localhost:7001 复制代码
集成swagger-ui文档
-
安装依赖
npm install egg-swagger-doc-feat -S 复制代码
-
config/plugin.js
中声明插件/** @type Egg.EggPlugin */ module.exports = { swaggerdoc: { enable: true, package: 'egg-swagger-doc-feat', }, }; 复制代码
-
config/config.default.js
文件中配置swaggerdoc插件'use strict'; module.exports = appInfo => { const config = (exports = {}); config.keys = appInfo.name + '_1622015186058_5727'; // add your middleware config here config.middleware = []; // add your user config here const userConfig = { // 配置swagger-ui接口文档 swaggerdoc: { dirScanner: './app/controller', //指定swaggerdoc从哪个目录开始检索, apiInfo: { title: 'xxx项目接口', description: 'xxx项目接口 swagger-ui for egg', version: '1.0.0', }, //接口文档主要信息、描述、版本号 schemes: ['http', 'https'], //协议 consumes: ['application/json'], //输出方式 produces: ['application/json'], enableSecurity: false, //enableValidate:true,//是否开启参数校验(很遗憾虽然有这个api,但是功能没有实现) routerMap: true, //是否自动注册路由(很香) enable: true, }, }; return { ...config, ...userConfig, }; }; 复制代码
-
某一个接口编写jsdoc
- 示例:在
app/controller/home.js
文件中const Controller = require ('egg').Controller; /** * @Controller 首页 */ class HomeController extends Controller { /** * @summary 首页 * @description 获取首页数据 * @router get /api/home/index * @request body indexRequest *body * @response 200 baseResponse 请求成功 */ async index () { const {ctx} = this; ctx.body = 'hi, egg'; } } module.exports = HomeController; 复制代码
- 示例:在
-
配置接口返回值的约束
注意:除了
contract
的文件位置和名字固定之外,像新建的index.js、home.js都不需要对应,只需要module.exports就行,我这样写是为了文件对应模块。- 新建
app/contract/index.js
文件(contract文件名不要写错,这个是swagger默认读取model的文件夹)module.exports = { baseRequest: {}, baseResponse: {//@response 200 baseResponse 操作结果,名字与相应结果对应 code: {type: 'integer', required: true, example: 0}, data: { type: 'string', example: '请求成功', }, errorMessage: {type: 'string', example: '请求成功'}, }, }; 复制代码
- 新建
app/contract/home.js
文件module.exports = { indexRequest: { test: {type: 'string', require: true, description: '测试'}, }, }; 复制代码
- 新建
-
访问http://127.0.0.1:7002/swagger-ui.html (我的项目是7002,一般默认是7001)
集成异常捕获系统
对于一个成熟的系统来说,异常捕获系统是必要的,
可以从以下俩个方面来考虑:
- 有些错误在生产环境是不能抛给用户的,容易造成系统的安全问题
- 可以利用洋葱圈模型,做一个全局的异常捕获中间件
编写
-
新建
app/middleware/error_handler.js
module.exports = (options, app) => { return async (ctx, next) => { try { await next (); } catch (err) { ctx.app.emit ('error', err, ctx); const status = err.status || 500; // 生产环境时 500 错误的详细错误内容不返回给客户端,因为可能包含敏感信息 const error = status === 500 && ctx.app.config.env === 'prod' ? 'Internal Server Error' : err.message; // 从 error 对象上读出各个属性,设置到响应中 ctx.body = {error}; if (status === 422) {//422是参数验证类型的错误 ctx.body.detail = err.errors; } ctx.status = status; } }; }; 复制代码
-
在
config/config.default.js
中挂载全局中间件'use strict'; module.exports = appInfo => { const config = (exports = {}); config.keys = appInfo.name + '_1622015186058_5727'; // 挂载应用级的中间件 config.middleware = ["errorHandler"]; // 添加用户自定义配置 const userConfig = { // 配置swagger-ui接口文档 swaggerdoc: { dirScanner: './app/controller', //指定swaggerdoc从哪个目录开始检索, apiInfo: { title: 'xxx项目接口', description: 'xxx项目接口 swagger-ui for egg', version: '1.0.0', }, //接口文档主要信息、描述、版本号 schemes: ['http', 'https'], //协议 consumes: ['application/json'], //输出方式 produces: ['application/json'], enableSecurity: false, routerMap: true, //是否自动注册路由(很香) enable: true, }, }; return { ...config, ...userConfig, }; }; 复制代码
验证
-
修改
app/controller/home.js
文件,主动抛出错误,验证我们的errorHandler
中间件。const Controller = require ('egg').Controller; /** * @Controller 首页 */ class HomeController extends Controller { /** * @summary 首页 * @description 获取首页数据 * @router get /api/home/index * @request body indexRequest *niji * @response 200 baseResponse 请求成功 */ async index () { const {ctx} = this; aaaaaaaaaaaaaaaaaaaaaaa ();//这个方法不存在 ctx.body = 'hi, egg'; } } module.exports = HomeController; 复制代码
-
访问这个接口,成功!
集成结构化返回数据
我们在写一个接口返回数据的时候,数据的结构必须一直,我们可以这样写:
ctx.body = {name:"xxx",age:34}
复制代码
但是如果每个函数都这样去写的话,就会很烦,我们需要一个统一的方法,来帮我们做这件事情:
-
新建
app/extend/helper.js
文件exports.success = ({ctx, res, code, message}) => { ctx.body = { code: code || 100, data: res, message: message || '请求成功', }; ctx.status = 200; }; 复制代码
-
app/controller/home.js
文件中,使用helper
apiconst Controller = require ('egg').Controller; /** * @Controller 首页 */ class HomeController extends Controller { /** * @summary 首页 * @description 获取首页数据 * @router get /api/home/index * @request body indexRequest *niji * @response 200 baseResponse 请求成功 */ async index () { const {ctx} = this; ctx.helper.success ({ctx, res: {value: 1}, message: '请求成功'}); } } module.exports = HomeController; 复制代码
-
请求接口,查看返回数据
集成参数校验功能
- 安装依赖
npm install egg-validate -S 复制代码
config/plugin.js
文件中配置'use strict'; /** @type Egg.EggPlugin */ module.exports = { // had enabled by egg // static: { // enable: true, // } swaggerdoc: { enable: true, package: 'egg-swagger-doc-feat', }, eggvalidaate: { enable: true, package: 'egg-validate', }, }; 复制代码
第一种:egg-validate
正常使用
一般我们正常使用
egg-validate
,就是用下面的这种方式,因为在你安装了egg-validate
,应用启动的时候,就会给ctx
这个上下文对象挂载validate
方法
-
在
app/controller/home.js
文件中const Controller = require ('egg').Controller; /** * @Controller 首页 */ class HomeController extends Controller { /** * @summary 首页 * @description 获取首页数据 * @router get /api/home/index * @request body indexRequest *niji * @response 200 baseResponse 请求成功 */ async index () { const {ctx} = this; ctx.validate ({ id: {type: 'number', required: true}, phoneNumber: {type: 'string', requried: true, format: /^1[34578]\d{9}$/}, }); ctx.helper.success ({ctx, res: {value: 1}, message: '请求成功'}); } } module.exports = HomeController; 复制代码
-
请求接口,进行测试
第二种:egg-validate
结合egg-swagger-doc-feat
的配置使用
我们正常使用egg-validate
的时候,都需要给ctx.validate({})
传递一个rule对象,这样会很烦。
现在我们提出这样一个设想,能不能把app/contract
文件夹下面定义的rule规则,给ctx.validate({})
传进去,(egg-validate
的规则属性和egg-swagger-doc-feat
的规则属性配置是一样的)这样我们共用一个规则,也方便我们后期维护。
通过debug发现,egg-swagger-doc-feat
这个npm包,虽然没有实现参数校验的功能(文章上面在配置中提到了)但是,他会把app/contract
下面定义的所有规则全部挂载到上下文对象ctx.rule
这个属性上,是不是很香。
既然如此我们就可以这样编写我们的代码:
-
在
app/controller/home.js
文件中const Controller = require ('egg').Controller; /** * @Controller 首页 */ class HomeController extends Controller { /** * @summary 首页 * @description 获取首页数据 * @router get /api/home/index * @request body indexRequest *niji * @response 200 baseResponse 请求成功 */ async index () { const {ctx} = this; ctx.validate (ctx.rule.indexRequest); ctx.helper.success ({ctx, res: {value: 1}, message: '请求成功'}); } } module.exports = HomeController; 复制代码
-
请求接口,进行测试
完美!!!