邊寫邊學系列(一) —— 使用apidoc,搞定自動化文檔

寫在前面

又想寫一個系列文章了,之因此起這麼個標題就是徹底從我自身實踐出發,我我的的感受就是,學習一個新知識特別是技術類的,若是隻是咬文嚼字,從頭至尾的擼一遍文檔,下來以後仍是不會達到應用級別,在嘗試的時候仍是不斷的中斷,而後再回頭從新看,我以爲效率不高。因此,我通常的作法就是直接上手,畢竟如今的前端技術都差很少,大同小異。框架語法+路由+自身的一些前置約束,隨便跑一個demo感受就差很少能夠上手了。至於更深層次的東西,能夠寫的時候遇到問題了再去深究,這樣的話知識點也掌握得更牢固。html

固然,每一個人的習慣都不一樣,我只是給這一系列文章搞一個噱頭,哈哈😄。前端

什麼是apiDoc

容我臭美一下,我看了一些apidoc的文章,我的以爲本身這篇做爲入門級應該是最詳實的了,不管是文章的結構仍是示例代碼,但願朋友們耐心看完😄node

仍是正常操做,雖然我說了喜歡邊寫邊學,可是首先仍是要打開官網去看看基本的知識的,至少你得知道這東西是幹什麼的,基於什麼,有什麼規範等等之類的內容。apiDoc官網介紹了 —— Inline Documentation for RESTful web APIs。翻譯過來就是「之內聯文檔的形式提供RESTful web APIs」。官網也是話很少說,上來直接就是各類Demo,由於是前端,我只關心JavaScript的了:python

其實apidoc支持不少語言,Java、PHP、python和JavaScript等。git

/**
 * @api {get} /user/:id Request User information
 * @apiName GetUser
 * @apiGroup User
 *
 * @apiParam {Number} id Users unique ID.
 *
 * @apiSuccess {String} firstname Firstname of the User.
 * @apiSuccess {String} lastname  Lastname of the User.
 */
複製代碼

上面就是一個內聯的api文檔,能夠看到,其實apidoc是經過咱們在代碼裏插入必定規範的註解(註釋),而後再經過運行相應的命令去解析,幫助咱們生成RESTful web APIs。這種方式也就意味着約束條件不少,咱們必須嚴格按照約束條件來作,固然好處也是有的,就是不會出錯,約定強也就意味着規範性強。github

apidoc的基本註釋規範

前面說過了,約束越強,後面寫起來其實也就越簡單,由於沒什麼創造性的東西,而apidoc的約束也就是各類@apiParam了。web

如上圖,其實也沒想象的那麼多,只要咱們把這些所有掌握了,基本就OK了。npm

邊寫邊學

既然是邊寫邊學,與其餘文章不一樣的地方就在於,不是一點一點的每個@api按照官方文檔翻譯一下,而是直接拿來用,逐個理解,從示例中去理解的效率要高的多得多得多。因此我準備是優先寫出來一個簡易的demo,而後不斷的加深,而後把全部的apiParam都過一遍,這篇文章就結束了,伴隨着示例代碼,你們看着也會舒服。學起來有代碼也簡單~json

第一步 - 安裝apidoc

萬變不離其宗,你既然要用它確定得先安裝它。後端

npm install -g apidoc

// 其實我想了一下,安裝在每一個項目裏的devDependencies也是OK的
npm install --save-dev apidoc
複製代碼

第二步 - 配置相關文件

由於apidoc最後會給咱們一個靜態文件,咱們能夠進行訪問,那麼關於這個服務的相關配置咱們能夠經過apidoc.json來進行。

這裏有一個前提,就是你得有一個本身的工程,我直接經過node起了一個服務,一個很是簡單的小工程,專門用來放這篇文章的Demo,apidoc-demo,喜歡的能夠給個🌟

// apidoc.json
{
  "name": "apidoc-demo",
  "version": "1.0.0",
  "description": "邊寫邊學系列 —— apidoc",
  "title": "apidoc-demo",
  "url" : "http://localhost:3333",
  "preview-url": "http://localhost:3333/apidoc/index.html" //預覽服務地址
}
複製代碼

name、version、description是基本的配置字段,其餘的看本身的方便來配,用不用得上再說。

第三步 - 第一個示例

OK,激動人心的時刻要到來了,邊寫邊學的興奮之處就在於,你其實還不太理解這個東西的工做原理和過程,經過Demo就把示例跑出來了,這表示你已經能夠成功寫它了,接下來你只須要深刻了解一下就能夠徹底掌握了~

// 咱們在routes/users.js下面寫一個api
/**
 * @api {get} /users 
 * @apiDescription 獲取用戶列表
 * @apiSuccessExample {json} Success-Response:
 * HTTP/1.1 200 OK
 *  {
 *      "errcode" : 0,
 *      "message": "",
 *      "data" : [{
 *          "name" : "userName",
 *          "email" : "userEmail"
 *      }]
 *  }
 * @apiSampleRequest http://localhost:3333/users
 * @apiVersion 1.0.0
 */
router.get('/', function(req, res, next) {
  res.json({
    errcode: 0,
    message: '',
    data: [
      {
        name: 'luffy',
        email: 'luffy@163.com'
      }, {
        name: 'naruto',
        email: 'naruto@126.com'
      }
    ]
  });
});
複製代碼

上面咱們寫好了一個獲取用戶列表的api,而後咱們來生成api文檔。

這裏有兩個前置條件要說明一下

  • 第一個,咱們在pulic/目錄下新建apidoc文件夾
  • 第二個,咱們運行apidoc -i routes/ -o public/apidoc/命令

解釋一下,咱們聲稱文檔的命令是apidoc -i routes/ -o public/apidoc/,熟悉node的同窗應該都清楚,routes就是後端路由,也就是api的位置,apidoc監聽的是routes/目錄的全部文件,而後輸出到public/apidoc/目錄中,這裏其實隨意,你輸出到哪裏均可以,由於它的輸出就是一套靜態文件,帶樣式的html。那麼既然是node服務,我起的靜態服務器就是public,我將生成的文件放到public/apidoc/文件夾下,項目啓動其實服務也就能夠被訪問了,一箭雙鵰,很方便。因此上面apidoc.json我寫了preview-url: http://localhost:3333/apidoc/index.html

由於命令很長,方便往後封裝一下:

// package.json
...
"scripts": {
    "start": "DEBUG=apidoc-demo:* nodemon ./bin/www",
+   "apidoc": "apidoc -i routes/ -o public/apidoc/"
}
...
複製代碼

咱們運行yarn apidoc,控制檯會輸出以下內容表示已經完成

而且/punlic/apidoc/目錄內也出現了apidoc爲咱們生成的內容:

而後咱們啓動服務yarn start,訪問http://localhost:3333/apidoc/index.html

點擊發送,還能夠看到該接口在目前狀態的返回值。
能夠看到,咱們的apidoc已經正常使用了,很是簡單~咱們來捋一下目前用到的參數

  • @api - 定義這是一個apidoc的api

    用法:`@api {method} path [title]`
    
    Required,這是必須的,每個apidoc的API文檔必須擁有此ziduan
    複製代碼
  • @apiDescription - 這個api的描述

    用法:@apiDescription text
    描述這個api是幹什麼的
    複製代碼
  • @apiSuccessExample - 成功示例

    用法: @apiSuccessExample [{type}] [title] example
    響應成功的返回示例
    複製代碼
  • @apiSampleRequest - 請求地址

    用法: @apiSampleRequest url
    用來點擊發送示例請求的地址
    複製代碼
  • @apiVersion - api版本號

    用法: @apiVersion version
    當前api的版本號
    複製代碼

第四步 - 進階學習

上面第一個例子相信你們都跑成功了,並且應該也掌握了幾個基本的字段意義以及如何使用。接下來咱們就進階,擴展一下api參數來豐富咱們的文檔。首先咱們來看看那麼一大串是什麼個東東,看起來真是醜啊。。。

擴展功能 —— 分組 + 響應參數

讀一下,大概意思就是接口的位置了,不過這麼長確實有點不美觀了,按照咱們的習慣,/routes/users.js應該就是專門爲User來提供接口的,而且按照以往接口規範,接口也是應該分組的。因此咱們來了,擴展接口功能 —— 分組

  • 分組 - @apiGroup
    /**
      * @api {get} /users 
      * @apiDescription 獲取用戶列表
    + * @apiGroup User               
      * @apiSuccessExample {json} Success-Response:
      * HTTP/1.1 200 OK
      *  {
      *      "errcode" : 0,
      *      "message": "",
      *      "data" : [{
      *          "name" : "userName",
      *          "email" : "userEmail"
      *      }]
      *  }
      * @apiSampleRequest http://localhost:3333/users
      * @apiVersion 1.0.0
      */
    複製代碼

同時,咱們在上面能夠看到,成功時候的返回,可是返回的數聽說明沒有,繼續擴展,返回數據格式及說明擴展接口功能 - 響應數據規範

  • 響應 - @apiSuccess
    /**
      * @api {get} /users 
      * @apiDescription 獲取用戶列表
      * @apiName GetUserList
      * @apiGroup User
    + * @apiSuccess {array} data 響應數據
    + * @apiSuccess {string} message 響應消息
    + * @apiSuccess {number} errcode 錯誤碼(本身定義,0爲無錯誤)
      * @apiSuccessExample {json} Success-Response:
      * HTTP/1.1 200 OK
      *  {
      *      "errcode" : 0,
      *      "message": "",
      *      "data" : [{
      *          "name" : "userName",
      *          "email" : "userEmail"
      *      }]
      *  }
      * @apiSampleRequest http://localhost:3333/users
      * @apiVersion 1.0.0
      */
    複製代碼

OK,增長完了,咱們再來從新生成一下文檔並重啓服務yarn apidoc && yarn start

能夠看到,咱們擴展的功能按照預期都出現了,至於這兩個的更全面使用,好比響應參數的default-value等等給大家擴展空間,去官網看吧~

擴展功能 - 錯誤響應 + 攜帶參數

上面的示例咱們看到了,擴展了兩個功能,能夠說很接近一個完整的api文檔了,不過既然有successExample,那麼也就應該有errorExample,由於畢竟不是全部請求都能成功,也存在失敗的響應嘛。

還有就是,第一個接口咱們獲取的是全部用戶列表,直接GET /users就OK了,那麼問題來了,若是有參數該怎麼辦呢,參數應該如何定義呢。因此接下來就寫一個帶參數的接口,咱們來把功能繼續完善。

/**
 * @api {get} /users/:id GetUserInfoById
 * @apiDescription 獲取用戶列表
 * @apiName GetUserById
 * @apiGroup User
 * @apiParam {Number} id Users unique ID.
 * @apiSuccess {Object} data 響應數據
 * @apiSuccess {String} message 響應消息
 * @apiSuccess {Number} errcode 錯誤碼(本身定義,0爲無錯誤)
 * @apiSuccessExample {Json} Success-Response:
 * HTTP/1.1 200 OK
 *  {
 *      "errcode" : 0,
 *      "message": "",
 *      "data" : {
 *          "id": 0,
 *          "name" : "userName",
 *          "email" : "userEmail"
 *      }
 *  }
 *  @apiError {4XX} UserNotFound The <code>id</code> of the User was not found.
 *  @apiErrorExample {json} Error-Response:
 *  HTTP/1.1 404 Not Found
 *  {
 *    "error": "UserNotFound"
 *  }
 * @apiSampleRequest http://localhost:3333/users/:id
 * @apiVersion 1.0.0
 */
router.get('/:id', function(req, res, next) {
  const { id } = req.params;
  if (!userData.some(item => item.id === parseInt(id, 10))) {
    // 不存在直接404
    return res.status(404).json({
      errcode: 404,
      message: `The ${id} of users was not found!`,
      data: {}
    });
  }
   // 存在
   res.json({
    errcode: 0,
    message: '',
    data: userData.find(item => item.id === parseInt(id, 10))
  })
});

複製代碼

代碼雖然有點長,不過大部分都是apidoc的註釋,咱們能夠看到,增長了兩個註解,一個@apiParam一個@apiErrorExample,也很簡單,一個是參數,一個是錯誤響應示例。

  • @apiParam - 參數

    用法:@apiParam [(group)] [{type}] [field=defaultValue] [description]
    請求參數,類型,能夠包括默認值和參數描述
    複製代碼
  • @apiError - 定義錯誤信息

    用法:@apiError [(group)] [{type}] field [description]
    定義錯誤類型,如 {4XX}錯誤,{5XX}錯誤
    複製代碼
  • @apiErrorExample - 錯誤響應示例

    用法: @apiErrorExample [{type}] [title]
                  example
    錯誤響應的示例
    複製代碼

咱們來看一下實際效果:

咱們發送一個錯誤請求,由於3是找不到的,因此返回的是404 Error。成功請求以下:

高級處理 - @apiParam既可做爲param又能夠做爲query

這裏算是一個小tip吧,也算是踩坑過程經歷到的一個東西,感受挺有意思,能夠這麼用。就是我在使用的時候一直有個疑惑,爲何註解裏只有@apiParam而沒有@apiQuery呢,由於實際場景中仍是有不少query形式的api的,可是說實話真沒發現,雖然RESTful形式的api放在param裏也能夠,不過仍是很疑惑。(若是有大牛給我解釋一下仍是很感激的)

// @apiParam -> @apiQuery
上面那個是個假命題,也就是仍是@qpiParam註解,可是其實是能夠看成query來去作的。仍是以查用戶內容做爲示例
/**
 * @api {get} /users/:id GetUserInfoById
 * @apiDescription 獲取用戶列表
 * @apiParam {Number} id
 * ...
 * @apiSampleRequest http://localhost:3333/users
 * @apiVersion 1.0.0
 */
複製代碼

咱們在上面這個地方;

將原來的: @apiSampleRequest: http://localhost:3333/users/:id
改爲 =>    @apiSampleRequest: http://localhost:3333/users
而後也會產生一個param爲id字段,此時咱們填寫進去id,在後臺就會將這個param轉換爲query的形式
複製代碼

【注】:官方文檔並無說這麼用,只是我使用起來發現表現是一致的。

擴展功能 - @apiDefine抽離公共塊 + @apiUse使用公共塊

到上面爲止,註解基本已經可使用的差很少了,可是有一個問題,若是想寫的很全,每個api上方的註釋會超級的長,怎麼辦呢?這就用到了擴展功能 - @apiDefine和@apiUse

  • @apiDefine - 定義代碼塊
    用法:@apiDefine name [title]
                      [description]
    定義公共代碼塊,而後能夠經過@apiUse使用
    複製代碼
  • @apiUse - 使用預先定義的代碼塊
    用法:@apiUse name
    使用@apiDefine定義好的代碼塊
    複製代碼

咱們仍是舉例說明,好比上面能夠抽離的部分,很明顯,成功返回字段是能夠抽離的,由於成功必定會返回兩個字段errcode,message,data字段因爲返回內容類型不肯定不是很好肯定,因此不作抽離。因此就抽離一個成功返回字段的代碼塊來使用。

/**
     * @apiDefine CommonSuccess 成功響應字段公共部分
     * @apiSuccess {Number} errcode The success res code.
     * @apiSuccess {Strng} message The res message.
     */
     
     // 在下面使用
     
     /**
     * @api {get} /users GetUserList
     * @apiDescription 獲取用戶列表
     * @apiName GetUserList
     * @apiGroup User
   + * @apiUse CommonSuccess
     * @apiSuccess {Array} data 響應數據
     * @apiSuccessExample {json} Success-Response:
     * HTTP/1.1 200 OK
     *  {
     *      "errcode" : 0,
     *      "message": "",
     *      "data" : [{
     *          "id": 0,
     *          "name" : "userName",
     *          "email" : "userEmail"
     *      }]
     *  }
     * @apiSampleRequest http://localhost:3333/users
     * @apiVersion 1.0.0
     */
複製代碼

其餘註解說明

除了上面的註解以外,還剩下的一些其餘可能會用到的註解,這裏順便也說一下。

  • @apiHeader - 頭部字段(相似param,只不過是放在req的頭部)
    用法:@apiHeader [(group)] [{type}] [field=defaultValue] [description]
    放在req的頭部,通常是用來進行校驗,如jwt
    複製代碼

好比,我在獲取全部用戶列表的接口裏要求頭部必須有authorization字段。

/**
  * @api {get} /users GetUserList
  * @apiDescription 獲取用戶列表
  * @apiName GetUserList
  * @apiGroup User
+ * @apiHeader {String} Authorization 用戶權限驗證碼 
  * @apiUse CommonSuccess
  * @apiSuccess {Array} data 響應數據
  * @apiSuccessExample {json} Success-Response:
  * HTTP/1.1 200 OK
  *  {
  *      "errcode" : 0,
  *      "message": "",
  *      "data" : [{
  *          "id": 0,
  *          "name" : "userName",
  *          "email" : "userEmail"
  *      }]
  *  }
+ * @apiUse InvalidToken
  * @apiSampleRequest http://localhost:3333/users
  * @apiVersion 1.0.0
*/
複製代碼

如圖所示,在模擬請求的同時會要求將token和header放進去。

  • @apiPermission - 有權限的api
    用法:@apiPermission name
    好比,某些api要求必須管理員才能訪問,或者要求頭部必須anthorization等。
    複製代碼

一樣,咱們讓獲取用戶列表增長permission提示:

// 第一步,定義一個token
 /**
 * @apiDefine token 須要驗證用戶權限
 * 須要在header中加入Authorization字段進行用戶權限驗證 
 */
 // 第二步,使用@apiPermission
 @apiPermission token
複製代碼

  • @apiIgnore - 忽略某些API,好比某些未完成的方法
    用法: @apiIgnore [hint]
    好比某些方法未完成不想暴露給外面,就是用這個註解
    複製代碼

咱們直接在代碼裏新寫一個api而後標記爲@apiIgnore:

/**
   * @apiIgnore 沒寫完的POST USER
   * @api {post} /users
   */
  router.post('/users', function (req, res, next) {
    console.log('沒寫完的POST USER');
  });
複製代碼

如圖所示能夠看到,咱們的文檔仍然是隻有兩個api,這個post api確實被ignore了。

抽離公共代碼塊

這裏就不是一個註解了,就是我認爲項目規範裏值得提一點的地方,就是說其實咱們抽離出來的代碼塊是能夠統一管理的,而沒必要要每一個文件都單獨管理。單獨抽離出來我統一放到了/routes/apidoc/common.js裏面,而後其餘路由文件正常使用就能夠,看起來項目總體就更規範了。仁者見仁智者見智,當項目龐大的時候,還能夠將響應再單獨封裝,這就看本身的須要了~

擴展插件 - vscode-apidoc-snippets

其實就是一個插件幫助咱們快速書寫apiodc的註釋部分,不過我安裝了也沒怎麼用,由於基本寫完一個其餘的複製粘貼改吧改吧就差很少了,插件提高的速度也不是很高甚至不高。哈哈,不過仍是列出來,萬一有人須要呢~

總結

最後,強調一下,其實我是爲了寫這篇文章隨便起了一個node服務,其實這個服務不適合非常,或者說這個場景不是很合適,爲何呢?由於node+渲染引擎的這種開發模式先後端原本就一我的來寫,並且接口與頁面耦合的很嚴重,一我的去寫其實可能來講沒有必要須要api文檔或者說場景不是很合適。我卻是以爲很適合先後端分離,node端做爲後端,雖然也多是一我的去寫可是可能分離的比較完全~

這個系列的第一篇文章,寫得還算比較流暢,最主要的是我確實是一邊寫代碼一邊學習apidoc一邊寫文章,三位一體感受學的仍是挺深入的。與其說是一篇文章,更不如說是一個記錄過程,不過這個過程我以爲可讓不少小白少走不少彎路,至少有完整的示例代碼,有詳細的學習過程,按部就班,仍是比較適合新手的~

apidoc-demo代碼地址,各位看官,有任何意見均可以提,但願多多關注多多喜歡。

相關文章
相關標籤/搜索