In this article, you can learn:

  • Generate a Swagger-UI document in the egg

Create a project

Egg. Js official document portal

  • Install dependencies:
    $ mkdir egg-example && cd egg-example
    $ npm init egg --type=simple // Create the project using the official scaffolding (simple: standard template)
    $ npm i
    Copy the code
  • Start the project
    $ npm run dev
    $ open http://localhost:7001
    Copy the code

Integrate Swagger-UI documentation

  • Install dependencies

    npm install egg-swagger-doc-feat -S
    Copy the code
  • Declare plug-ins in config/plugin.js

    / * *@type Egg.EggPlugin */
    module.exports = {
      swaggerdoc: {
        enable: true.package: 'egg-swagger-doc-feat',}};Copy the code
  • Config /config.default.js file to configure the SwaggerDoc plug-in

    '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 = {
        // Configure swagger- UI interface document
        swaggerdoc: {
          dirScanner: './app/controller'.// Specify the directory where swaggerDoc is retrieved,
          apiInfo: {
            title: 'XXX project interface'.description: 'SWAGger - UI for egg'.version: '1.0.0',},// The main information, description, and version number of the interface document
          schemes: ['http'.'https']./ / agreement
          consumes: ['application/json'].// Output mode
          produces: ['application/json'].enableSecurity: false.//enableValidate:true,// whether to enable parameter validation.
          routerMap: true.// Whether to automatically register routes (very fragrant)
          enable: true,}};return{... config, ... userConfig, }; };Copy the code
  • Write jSDoc for an interface

    • Example: inapp/controller/home.jsIn the file
      const Controller = require ('egg').Controller;
      / * * *@Controller Home page * /
      class HomeController extends Controller {
        / * * *@summary Home page *@description Get home page data *@router get /api/home/index
         * @request body indexRequest *body
         * @response 200 baseResponse request succeeded */
        async index () {
          const {ctx} = this;
          ctx.body = 'hi, egg'; }}module.exports = HomeController;
      Copy the code
  • The restriction on the return value of the interface is configured

    }}}}}}}}}}}}}}}}}}}}}}}}

    • newapp/contract/index.jsFile (do not write the contract name wrong, this is the default folder to read model)
      module.exports = {
       baseRequest: {},
       baseResponse: {//@response 200 baseResponse Operation result. The name corresponds to the corresponding result
         code: {type: 'integer'.required: true.example: 0},
         data: {
           type: 'string'.example: 'Request successful',},errorMessage: {type: 'string'.example: 'Request successful',}}};Copy the code
    • newapp/contract/home.jsfile
      module.exports = {
        indexRequest: {
          test: {type: 'string'.require: true.description: 'test',}}};Copy the code
  • Visit http://127.0.0.1:7002/swagger-ui.html (my project is 7002, generally the default is 7001)

Integrated exception capture system

For a mature system, an exception catching system is necessary,

This can be considered in two ways:

  1. Some errors in the production environment can not be thrown to the user, easy to cause system security problems
  2. The onion ring model can be used to make a global exception catching middleware

write

  • The new 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;
          // The detailed error contents of the 500 error in the production environment are not returned to the client because they may contain sensitive information
          const error = status === 500 && ctx.app.config.env === 'prod'
            ? 'Internal Server Error'
            : err.message;
    
          // Read each property from the error object and set it in the response
          ctx.body = {error};
          if (status === 422) {//422 is a parameter validation type errorctx.body.detail = err.errors; } ctx.status = status; }}; };Copy the code
  • Mount the global middleware in config/config.default.js

    'use strict';
    module.exports = appInfo= > {
      const config = (exports = {});
      config.keys = appInfo.name + '_1622015186058_5727';
    
      // Mount the application-level middleware
      config.middleware = ["errorHandler"];
    
      // Add the user-defined configuration
      const userConfig = {
        // Configure swagger- UI interface document
        swaggerdoc: {
          dirScanner: './app/controller'.// Specify the directory where swaggerDoc is retrieved,
          apiInfo: {
            title: 'XXX project interface'.description: 'SWAGger - UI for egg'.version: '1.0.0',},// The main information, description, and version number of the interface document
          schemes: ['http'.'https']./ / agreement
          consumes: ['application/json'].// Output mode
          produces: ['application/json'].enableSecurity: false.routerMap: true.// Whether to automatically register routes (very fragrant)
          enable: true,}};return{... config, ... userConfig, }; };Copy the code

validation

  • Modify the app/controller/home.js file to actively throw an error and validate our errorHandler middleware.

    const Controller = require ('egg').Controller;
    / * * *@Controller Home page * /
    class HomeController extends Controller {
      / * * *@summary Home page *@description Get home page data *@router get /api/home/index
       * @request body indexRequest *niji
       * @response 200 baseResponse request succeeded */
      async index () {
        const {ctx} = this;
        aaaaaaaaaaaaaaaaaaaaaaa ();// This method does not exist
        ctx.body = 'hi, egg'; }}module.exports = HomeController;
    Copy the code
  • Access this interface, success!

Integrate structured return data

When we write an interface that returns data, the structure of the data must always be the same. We can write:

ctx.body = {name:"xxx",age:34}
Copy the code

But it would be annoying to write every function like this, and we need a uniform way to do it:

  • Create the app/extend/helper.js file

    exports.success = ({ctx, res, code, message}) = > {
      ctx.body = {
        code: code || 100.data: res,
        message: message || 'Request successful'}; ctx.status =200;
    };
    
    Copy the code
  • In the app/controller/home.js file, use the HelperAPI

    const Controller = require ('egg').Controller;
    / * * *@Controller Home page * /
    class HomeController extends Controller {
      / * * *@summary Home page *@description Get home page data *@router get /api/home/index
       * @request body indexRequest *niji
       * @response 200 baseResponse request succeeded */
      async index () {
        const {ctx} = this;
        ctx.helper.success ({ctx, res: {value: 1}, message: 'Request successful'}); }}module.exports = HomeController;
    Copy the code
  • Request the interface and view the returned data

Integrated parameter verification function

  • Install dependencies
    npm install egg-validate -S
    Copy the code
  • config/plugin.jsConfiguration in file
    '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',}};Copy the code

The first:egg-validateNormal use

We normally use egg-validate in the following way, because when you install egg-validate, the context object CTX will be mounted with the validate method when the application starts

  • In the app/controller/home.js file

    const Controller = require ('egg').Controller;
    / * * *@Controller Home page * /
    class HomeController extends Controller {
      / * * *@summary Home page *@description Get home page data *@router get /api/home/index
       * @request body indexRequest *niji
       * @response 200 baseResponse request succeeded */
      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: 'Request successful'}); }}module.exports = HomeController;
    Copy the code
  • Request the interface and test it

The second:egg-validateIn combination withegg-swagger-doc-featConfiguration use of

When we use egg-validate normally, we need to pass a rule object to ctx.validate({}), which is annoying.

Validate ({}) = “ctx.validate({})”; (The configuration of the rule attribute of egg-validate is the same as that of egg-swagger-doc-feat.) In this way, we share the same rule, which is convenient for our later maintenance.

The NPM package, egg-Swagger -doc-feat, does not implement parameter verification (as mentioned in the configuration above), but it does mount all the rules defined in app/ Contract to the context object ctx.rule.

In this case, we can write our code like this:

  • In the app/controller/home.js file

    const Controller = require ('egg').Controller;
    / * * *@Controller Home page * /
    class HomeController extends Controller {
      / * * *@summary Home page *@description Get home page data *@router get /api/home/index
       * @request body indexRequest *niji
       * @response 200 baseResponse request succeeded */
      async index () {
        const {ctx} = this;
        ctx.validate (ctx.rule.indexRequest);
        ctx.helper.success ({ctx, res: {value: 1}, message: 'Request successful'}); }}module.exports = HomeController;
    Copy the code
  • Request the interface and test it

Perfect!!