边写边学系列(一) —— 使用apidoc,搞定自动化文档

本文介绍了apidoc的使用,作者主张边写边学。先阐述了apidoc是通过代码注解生成RESTful web APIs,介绍了基本注释规范。接着详细说明了安装、配置文件、第一个示例的步骤,还进行了进阶学习,包括分组、响应参数、错误响应等扩展功能,最后总结其适用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写在前面

又想写一个系列文章了,之所以起这么个标题就是完全从我自身实践出发,我个人的感觉就是,学习一个新知识特别是技术类的,如果只是咬文嚼字,从头到尾的撸一遍文档,下来之后还是不会达到应用级别,在尝试的时候还是不断的中断,然后再回头重新看,我觉得效率不高。所以,我一般的做法就是直接上手,毕竟现在的前端技术都差不多,大同小异。框架语法+路由+自身的一些前置约束,随便跑一个demo感觉就差不多可以上手了。至于更深层次的东西,可以写的时候遇到问题了再去深究,这样的话知识点也掌握得更牢固。

当然,每个人的习惯都不同,我只是给这一系列文章搞一个噱头,哈哈?。

什么是apiDoc

容我臭美一下,我看了一些apidoc的文章,个人觉得自己这篇作为入门级应该是最详实的了,无论是文章的结构还是示例代码,希望朋友们耐心看完?

还是正常操作,虽然我说了喜欢边写边学,但是首先还是要打开官网去看看基本的知识的,至少你得知道这东西是干什么的,基于什么,有什么规范等等之类的内容。apiDoc官网介绍了 —— Inline Documentation for RESTful web APIs。翻译过来就是“以内联文档的形式提供RESTful web APIs”。官网也是话不多说,上来直接就是各种Demo,因为是前端,我只关心JavaScript的了:

其实apidoc支持很多语言,Java、PHP、python和JavaScript等。

/**
 * @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。这种方式也就意味着约束条件很多,我们必须严格按照约束条件来做,当然好处也是有的,就是不会出错,约定强也就意味着规范性强。

apidoc的基本注释规范

前面说过了,约束越强,后面写起来其实也就越简单,因为没什么创造性的东西,而apidoc的约束也就是各种@apiParam了。

如上图,其实也没想象的那么多,只要我们把这些全部掌握了,基本就OK了。

边写边学

既然是边写边学,与其他文章不同的地方就在于,不是一点一点的每一个@api按照官方文档翻译一下,而是直接拿来用,逐个理解,从示例中去理解的效率要高的多得多得多。所以我准备是优先写出来一个简易的demo,然后不断的加深,然后把所有的apiParam都过一遍,这篇文章就结束了,伴随着示例代码,大家看着也会舒服。学起来有代码也简单~

第一步 - 安装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代码地址,各位看官,有任何意见都可以提,希望多多关注多多喜欢。

转载于:https://juejin.im/post/5cdbafd6e51d45475e613e70

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值