Skip to content

RESTful API 设计

1. 为什么要使用 RESTful 架构?

  • REST 是 Representational State Transfer 的缩写,如果一个架构符合 REST 原则,就称它为 RESTful 架构
  • RESTful 架构可以充分的利用 HTTP 协议的各种功能,是 HTTP 协议的最佳实践
  • RESTful API 是一种软件架构风格、设计风格,可以让软件更加清晰,更简洁,更有层次,可维护性更好
API功能
GET /zoos列出所有动物园
POST /zoos新建一个动物园
GET /zoos/:id获取某个指定动物园的信息
PUT /zoos/:id更新某个指定动物园的全部信息
PATCH /zoos/:id更新某个指定动物园的部分信息
DELETE /zoos/:id删除某个动物园
GET /zoos/:id/animals列出某个指定动物园的所有动物
DELETE /zoos/:id/animals/:id删除某个指定动物园的指定动物

请求 = 动词 + 宾语

  • 动词 使用五种 HTTP 方法,对应 CRUD 操作
  • 宾语 URL 应该全部使用名词复数,可以有例外,比如搜索可以使用更加直观的 search
  • 过滤信息(Filtering)如果记录数量很多,API 应该提供参数,过滤返回结果
    • ?limit=10 指定返回记录的数量
    • ?offset=10 指定返回记录的开始位置
    • ?page=2&per_page=100 指定第几页,以及每页的记录数
    • ?sortby=name&order=asc 指定返回结果按照哪个属性排序,以及排序顺序
    • ?animal_type_id=1 指定筛选条件

参数的设计允许存在冗余,即允许 API 路径和 URL 参数偶尔有重复。比如,GET /zoo/ID/animalsGET /animals?zoo_id=ID 的含义是相同的。

2. API 请求

动词功能
GET读取(Read)
POST新建(Create)
PUT更新(Update)
PATCH更新(Update),通常是部分更新
DELETE删除(Delete)

下面都是错误的:

  • /getAllCars
  • /createNewCar
  • /deleteAllRedCars

3. API 响应

状态码:

  • 1xx 相关信息
  • 2xx 操作成功
  • 3xx 重定向
  • 4xx 客户端错误
  • 5xx 服务器错误

客户端的每一次请求,服务器都必须给出回应。回应包括 HTTP 状态码和数据两部分。

五大类状态码,总共 100 多种,覆盖了绝大部分可能遇到的情况。每一种状态码都有约定的解释,客户端只需查看状态码,就可以判断出发生了什么情况。API 不需要 1xx 状态码。

菜鸟:HTTP 状态码参考

状态码名称含义
400Bad Request服务器不理解客户端的请求,未做任何处理
401Unauthorized用户未提供身份验证凭据,或者没有通过身份验证
403Forbidden用户通过了身份验证,但是不具有访问资源所需的权限
404Not Found所请求的资源不存在,或不可用
405Method Not Allowed用户已经通过身份验证,但是所用的 HTTP 方法不在他的权限之内
410Gone所请求的资源已从这个地址转移,不再可用
415Unsupported Media Type客户端要求的返回格式不支持。比如,API 只能返回 JSON 格式,但是客户端要求返回 XML 格式
422Unprocessable Entity客户端上传的附件无法处理,导致请求失败
429Too Many Requests客户端的请求次数超过限额
  • 客户端请求时,要明确告诉服务器,接受 JSON 格式,请求的 HTTP 头的 ACCEPT 属性要设成 application/json
  • 服务端返回的数据,不应该是纯文本,而应该是一个 JSON 对象。服务器回应的 HTTP 头的 Content-Type 属性要设为 application/json
  • 错误处理 如果状态码是 4xx,就应该向用户返回出错信息。一般来说,返回的信息中将 error 作为键名,出错信息作为键值即可:{error: "Invalid API key"}
  • 认证 RESTful API 应该是无状态,每个请求应该带有一些认证凭证。推荐使用 JWT 认证,并且使用 SSL
  • Hypermedia 即返回结果中提供链接,连向其他 API 方法,使得用户不查文档,也知道下一步应该做什么

API 的身份认证应该使用 OAuth 2.0 框架。

4. 服务器和域名

API 与用户的通信协议,尽量使用 HTTPS 协议。

应该尽量将 API 部署在专用域名之下。

text
https://api.example.com

如果确定 API 很简单,不会有进一步扩展,可以考虑放在主域名下:

text
https://example.org/api/

应该将 API 的版本号放入 URL:

text
https://api.example.com/v1/

另一种做法是,将版本号放在 HTTP 头信息中,但不如放入 URL 方便和直观。也可以参考 GitHub 采取的做法

5. 路径

路径又称 “终点”(endpoint),表示 API 的具体网址。

在 RESTful 架构中,每个网址代表一种资源(Resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的 “集合”(Collection),所以 API 中的名词也应该使用复数。

举例来说,有一个 API 提供动物园(zoo)的信息,还包括各种动物和雇员的信息,则它的路径应该设计成下面这样:

text
https://api.example.com/v1/zoos
https://api.example.com/v1/animals
https://api.example.com/v1/employees

对于资源的具体操作类型,由 HTTP 动词表示。

常用的 HTTP 动词有五个,还有两个不常用的 HTTP 动词。

  • HEAD:获取资源的元数据
  • OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的

6. Hypermedia

RESTful API 最好做到 Hypermedia,即返回结果中提供链接,连向其他 API 方法,使得用户不查文档,也知道下一步应该做什么。

比如,当用户向 api.example.com 的根目录发出请求,会得到这样一个文档:

json
{
    "link": {
        "rel":   "collection https://www.example.com/zoos",
        "href":  "https://api.example.com/zoos",
        "title": "List of zoos",
        "type":  "application/vnd.yourformat+json"
    }
}

上面代码表示,文档中有一个 link 属性,用户读取这个属性就知道下一步该调用什么 API 了。rel 表示这个 API 与当前网址的关系(Collection 关系,并给出该 Collection 的网址),href 表示 API 的路径,title 表示 API 的标题,type 表示返回类型。

Hypermedia API 的设计被称为 HATEOAS 。Github 的 API 就是这种设计,访问 api.github.com 会得到一个所有可用 API 的网址列表。

json
{
  "current_user_url": "https://api.github.com/user",
  "authorizations_url": "https://api.github.com/authorizations",
  // ...
}

从上面可以看到,如果想获取当前用户的信息,应该去访问 api.github.com/user,然后就得到了下面结果:

json
{
  "message": "Requires authentication",
  "documentation_url": "https://developer.github.com/v3"
}

上面代码表示,服务器给出了提示信息,以及文档的网址。