RESTful API

项目中,经常要提供一个 API 供第三方使用,一个通用的 API 设计规范就是使用 RESTful API,RESTful API 是使用 HTTP 中的请求类型来标识对资源的操作。如:

  • GET /ticket 获取 ticket 列表
  • GET /ticket/:id 查看某个具体的 ticket
  • POST /ticket 新建一个 ticket
  • PUT /ticket/:id 更新 id 为 12 的 ticket
  • DELETE /ticket/:id 删除 id 为 12 的 ticekt

创建 RESTful Controller

可以通过 -r 参数来创建 REST Controller。如:

thinkjs controller user -r

会创建下面几个文件:

create : src/controller/rest.js
create : src/controller/user.js
create : src/logic/user.js

其中 src/controller/user.js 会继承 src/controller/rest.js 类,rest.js 是 RESTful Controller 的基类,具体的逻辑可以根据项目情况进行修改。

添加自定义路由

RESTful Controller 创建后并不能立即对其访问,需要添加对应的自定义路由,修改路由配置文件 src/config/router.js,添加如下的配置:

module.exports = [
  [/\/user(?:\/(\d+))?/, 'user?id=:1', 'rest'], // 第一种方式
  ['/user/:id?', '/user', 'rest'], // 第二种方式
  ['/user/:id?', 'rest'], // 第三种方式
]

注:第三种方式需要 think-router 的版本 >=1.0.17

上面自定义路由的含义为:

  • /\/user(?:\/(\d+))?/ URL 的正则
  • user?id=:1 映射后要解析的路由,:1 表示取正则里的 (\d+) 的值
  • rest 表示为 REST API

通过自定义路由,将 /user/:id 相关的请求指定为 REST Controller,然后就可以对其访问了。

  • GET /user 获取用户列表,执行 getAction
  • GET /user/:id 获取某个用户的详细信息,执行 getAction
  • POST /user 添加一个用户,执行 postAction
  • PUT /user/:id 更新一个用户,执行 putAction
  • DELETE /user/:id 删除一个用户,执行 deleteAction

如果有一系列路由都是 RESTful 路由的话,每次都添加自定义路由势必有些麻烦,这时候可以修改一下自定义路由的配置文件,例如:

module.exports = [
  [/\/api\/(\w+)(?:\/(\d+))?/, 'api/:1?id=:2', 'rest']
];

这样表示所有以 /api 开头的二级路由都会被指定成 RESTful 路由。

数据校验

Controller 里的方法执行时并不会对传递过来的数据进行校验,数据校验可以放在 Logic 里处理,文件为 src/logic/user.js,具体的 Action 与 Controller 里一一对应。具体的使用方式请见 Logic

子级 RESTful API

有时候有子级 RESTful API,如:某篇文章的评论接口,这时候可以通过下面的自定义路由完成:

module.exports = [
  [/\/post\/(\d+)\/comments(?:\/(\d+))?/, 'comment?postId=:1&id=:2', 'rest']
]

这样在对应的 Action 里,可以通过 this.get("postId") 来获取文章的 id,然后放在过滤条件里处理即可。

const Rest = require('./rest.js');
module.exports = class extends Rest {
  async getAction() {
    const postId = this.get('postId');
    const commentId = this.get('id');
    const comment = this.model('comment');
    if(commentId) { // 获取单条评论的详细信息
      const data = await comment.where({post_id: postId, id: commentId}).find();
      return this.success(data);
    } else { // 获取单条文章下的评论列表
      const list = await comment.where({post_id: postId}).select();
      return this.success(list);
    }
  }
}

多版本 RESTful API

有些 REST API 有时候前后不能完全兼容,需要有多个版本,这时候也可以通过自定义路由管理,如:

module.exports = [
  [/\/v1\/user(?:\/(\d+))?/, 'v1/user?id=:1', 'rest'], //v1 版本
  [/\/v2\/user(?:\/(\d+))?/, 'v2/user?id=:1', 'rest']  //v2 版本
]

这时候只要在 src/controller/ 下建立子目录 v1/v2/ 即可,执行时会自动查找,具体见 多级控制器

Mongo 的 RESTful API

由于 Mongo 的 id 并不是纯数字的,所以处理 Mongo 的 RESTful API 时只需要修改下对应的正则即可(将 \d 改为 \w):

module.exports = [
  [/\/user(?:\/(\w+))?/, 'user?id=:1', 'rest']
]

常见问题

怎么查看 RESTful API 的自定义路由已经生效?

有时候添加 RESTful Controller 和自定义路由后,访问并没有生效,这时候可以通过 DEBUG=think-router npm start 启动服务查看解析后的 controller 和 action 看其是否生效,具体请见怎么查看当前地址解析后的 controller 和 action 分别对应什么?

如发现文档中的错误,请点击这里修改本文档,修改完成后请 pull request,我们会尽快合并、更新。