边写边学系列(一) —— 使用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代码地址,各位看官,有任何意见均可以提,但愿多多关注多多喜欢。

相关文章
相关标签/搜索