1. 程式人生 > 實用技巧 >從零開始的野路子React/Node(7)將Swagger(OpenAPI)運用於後端API

從零開始的野路子React/Node(7)將Swagger(OpenAPI)運用於後端API

之前公司做專案是用過swagger來配置python模型的API,感覺非常好用。swagger可以提供request, response甚至error的驗證機制,十分便利。node當然也可以用啦。

我們需要使用的庫主要是swagger-ui-express,它將提供swagger的相關功能以及一個UI,方便檢視和除錯。

1、初始設定

老規矩,我們還是通過express work_with_swagger來新建一個叫work_with_swagger的專案。然後依舊是刪除bin資料夾,改改app.js以及將package.json中的start改為node app.js(當然,用nodemon會更好,可以安裝npm install nodemon,然後將start改為nodemon app.js)。

var express = require('express');

var app = express();

app.use(express.urlencoded({extended: true}));  //解析json用
app.use(express.json()); //解析json用

app.listen(5000, function() {
    console.log('App listening on port 5000...')
});

然後我們來做些設定:

我們會有一個Controller來負責request -> response的邏輯。

我們將原來的routes刪掉,增加自己的路由Routes,對接到對應的controller。

我們新增一個swagger資料夾,並在其中加入我們的配置檔案swagger.yml。

2、新增swagger-ui-express

現在,我們需要改動一下app.js來把swagger配置檔案包含進去。由於我們的swagger檔案是yaml檔案,所以我們需要yamljs庫來讀取它(預設只能讀取json格式的swagger檔案,直接抄https://www.npmjs.com/package/swagger-ui-express提供的方法)。

此外,我們把路由檔案也加入進去。

var express = require('express');
var swaggerUi = require('swagger-ui-express');
var YAML = require('yamljs'); const swaggerDocument = YAML.load('./swagger/swagger.yml'); const RandomRouter = require('./Routes/RandomRouter'); var app = express(); app.use(express.urlencoded({extended: true})); //解析json用 app.use(express.json()); //解析json用 app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); app.use('/', RandomRouter) app.listen(5000, function() { console.log('App listening on port 5000...') });

我們現在可以象徵性地寫幾個controllers並把他們加入路由檔案中,2個get,1個post:

var express = require('express');
var YAML = require('yamljs');

exports.healthCheck = (req, res) => {
    const swaggerDocument = YAML.load('./swagger/swagger.yml');
    res.status(200).send(
        {
            message:'耗子尾汁',
            version:swaggerDocument['info']['version']
        }
    )
};

exports.setAge = (req, res) => {
    var age = req.params.number
    res.status(200).send(`來騙,來偷襲,我${age}歲的老同志`)
};

exports.tricks = (req, res) => {
    var tricks = req.body.tricks
    var comment = req.body.comment

    var line = tricks.join(', ') + ', ' + comment
    res.status(200).send(line)
};
var express = require('express');
const RandomController = require('../Controllers/RandomController');

var router = express.Router();

router.get('/age/:number', RandomController.setAge);
router.post('/tricks', RandomController.tricks);
router.get('/', RandomController.healthCheck);

module.exports = router;

接下來,我們來配置一下swagger.yml(yaml檔案寫長了真是需要遊標卡尺……):

openapi: 3.0.0
info:
  title: Work with Swagger
  description: This is just an experiment
  version: v1.0

servers:
- url: http://localhost:5000

paths:
  /:
    get:
      summary: Health check endpoint
      operationId: healthCheck
      responses:
        200:
          description: Successful operation
        400:
          description: Bad request
        404:
          description: Not found

  /age/{number}:
    get:
      summary: Set the age of the user
      parameters:
      - name: number
        in: path
        description: The age of the user
        required: true
        schema:
          type: integer
          format: int64
      responses:
        200:
          description: Successful operation
        400:
          description: Bad request
        404:
          description: Not found

  /tricks:
    post:
      summary: Hit by some tricks
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/tricks'
      responses:
        200:
          description: Successful operation
        400:
          description: Bad request
        404:
          description: Not found

components:
  schemas:
    tricks:
      type: object
      required:
      - tricks
      - comment
      properties:
        tricks:
          type: array
        comment:
          type: string

這裡我們對每個路由節點做出了一些規定,比如對於/age/{number},我們規定number只能是整數;比如對於/tricks,傳入的json一定要包含tricks和comment等等(也就是規定了schema)。

這裡我們可以用2種不同的方式來規定schema:一種是直接在節點部分的schema下繼續下寫去(例如/age/{number}節點那樣);另一種是單獨寫在components的schemas下面,而在節點的schema處用$ref來指定引用(例如/tricks節點那樣)。

OK,差不多完工了,現在我們進入UI介面看一看效果,輸入http://localhost:5000/api-docs/,會出現這樣一個介面:

點開某個節點(此處為/age/{number}),就能看到我們對該節點的一些設定:

點選Try it out之後就可以輸入params,我們輸入一個整數試試,點選Execute:

如果我們試圖輸入string,會發現無法Execute:

看來效果不錯。但swagger-ui-express畢竟只是類似於一個文件工具,點到為止。我們在Postman中試試,會發現其實根本沒有驗證機制的存在……

3、補上驗證機制

雖然驗證要以和為貴,但不能光點到為止啊,看來我們還需要另一個驗證工具,那就是openapi-express-validator。

我們用npm install openapi-express-validator來完成安裝,然後再在app.js中加入一些內容:

var express = require('express');
var swaggerUi = require('swagger-ui-express');
var YAML = require('yamljs');
var OpenApiValidator = require('express-openapi-validator');

const swaggerDocument = YAML.load('./swagger/swagger.yml');
const RandomRouter = require('./Routes/RandomRouter');

var app = express();

app.use(express.urlencoded({extended: true}));  //解析json用
app.use(express.json()); //解析json用
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));

// 新增,指定一下swagger檔案
const spec = './swagger/swagger.yml';
app.use('/spec', express.static(spec));

// 將OpenApiValidator加入中介軟體
app.use(
  OpenApiValidator.middleware({
    apiSpec: './swagger/swagger.yml',
    validateRequests: true,
    //validateResponses: true, // 如果需要驗證response,則設為true
  }),
);

app.use('/', RandomRouter)

app.use((err, req, res, next) => {
  // 配置錯誤資訊
  res.status(err.status || 500).json({
    message: err.message,
    errors: err.errors,
  });
});

app.listen(5000, function() {
  console.log('App listening on port 5000...')
});

現在我們再來試試:

這次沒問題了,我們得到了一個錯誤資訊,提示我們params中的number應該是整數。

輸入整數則沒有問題,驗證功能完成了。

程式碼見:

https://github.com/SilenceGTX/work_with_swagger