Rest API 介绍以及最佳实践

2020-04-12 web

当前网络应用一般分成了独立的前后端,而且前端设备层出不穷,所以需要有统一的标准,便于不同的前后端进行通讯,而 RESTful API 是目前比较成熟的 API 设计理论。

它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。

restful api logo

简介

REST 是 Roy Thomas Fielding 在 2000 年的 博士论文 中提出的,也就是他对互联网软件的架构原则的总结,实际上其全称为 Representational State Transfer 。

他在论文中提到:“我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。REST 指的是一组架构约束条件和原则。” 如果一个架构符合 REST 的约束条件和原则,我们就称它为 RESTful 架构。

要理解 RESTful 架构,最好的方法就是去理解 Representational State Transfer 这个词组所代表的具体含义,进而可以体会 REST 是一种什么样的设计。

OpenAPI

这是一个标准的、与具体编程语言无关的 RESTful API 规范,只要遵循 OpenAPI 规范来定义 API,那么就可以用文档生成工具来展示 API,用代码生成工具来自动生成各种编程语言的服务器端和客户端的代码,用自动测试工具进行测试等等。

相关内容可以参考 官网 以及相关的协议 OpenAPI Specification,或者 aipfox 中的中文介绍。

最佳实践

1. 域名

一般是尽量将 API 部署在专有域名下,例如 https://api.example.com 或者 https://example.com/api 主域名的固定路径下。

2. 版本

通常是将版本信息放到 URL 中,也就是 https://api.example.com/v1/,另一种是将版本放到 HTTP 头中,不过不太直观。

3. 路径资源

路径也被称为 Endpoint,是 API 的具体网址,每个网址代表一种资源,因为 “资源” 表示一种实体,所以应该是名词,动词应该放在 HTTP 协议中。例如,某个 URI 是 /posts/show/cars,其中 show 是动词,这个 URI 就设计错了,正确的写法应该是 /posts/cars ,然后用 GET 方法表示 show

4. HTTP 动词

对于资源的具体操作类型由 HTTP 动词表示,有如下几种:

GET    (SELECT) 取出资源,可以是一项或多项。
POST   (CREATE) 新建一个资源。
PUT    (UPDATE) 更新资源,请求需要提供改变后的完整资源。
PATCH  (UPDATE) 更新资源,请求只需要提供改变的属性。
DELETE (DELETE) 删除资源。

5. 过滤数据

当记录数太多时可以通过 API 提供的参数进行过滤,如下是常见的使用方式。

?sort=name&order=asc 结果按照哪个属性排序,以及排序顺序
?name=foobar&id=1    指定筛选条件

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

分页逻辑

分页中比较耗时的是计算总数,包括了所有以及部分过滤条件的,需要遍历所有数据,虽然 MySQL 针对前者进行了优化,但是仍建议尽量避免,建议使用如下策略:

  1. 尽量避免总页查询,也就是分页不显示总数,首页按照条件查询,后续页通过 marker 参数传入上页最后一条记录的 ID 信息;好处是效率最高,但无法显示总数,无法跳转页查询。
  2. 首次查询总数,后续只是简单使用 limit, offset 方式查询。

如下介绍常用场景的 API 接口。

----- 按照顺序查询,适用于后台固定任务
GET /users?limit=10&total=false&order=id
GET /users?limit=10&total=false&order=id&marker=120
----- 默认是按照ID排序,无总数,所以可以简化为
GET /users?limit=10
GET /users?limit=10&marker=120

----- 按照正常分页查询
GET /users?limit=10&total=true&order=id
GET /users?limit=10&total=false&order=id&offset=10

另外,还有如下的注意事项:

  • 排序,默认按照 id 升序排序,也可以通过 order=xxx 以及 desc=true 选择字段按照降序排序。
  • 这类接口也允许通过 ID、UID 过滤某个具体用户,不过此时返回同样是数组。

6. 状态码

使用 HTTP 状态码处理错误,HTTP 状态码提供了 70 个出错,实际上可以只使用其中的 10 个左右,其中方括号中是该状态码对应的 HTTP 操作,

----- 2xx 成功,请求已被成功接收
200 OK              - [GET] 服务器成功返回用户请求的数据
201 Created         - [POST/PUT/PATCH] 用户新建或修改数据成功
202 Accepted        - [*] 表示一个请求已经进入后台排队,是异步任务
204 No Content      - [DELETE] 用户删除数据成功。

----- 4xx 客户端错误,请求有语法错误或请求无法实现
400 Bad Request     - [POST/PUT/PATCH] 用户发出的请求有错误,服务器没有进行新建或修改数据的操作
401 Unauthorized    - [*] 用户没有权限,一般是令牌、用户名、密码错误
403 Forbidden       - [*] 表示用户得到授权,但是禁止访问
404 Not Found       - [*] 用户发出的请求针对的是不存在的记录,服务器没有进行操作
406 Not Acceptable  - [GET] 用户请求的格式不对,例如希望 JSON 但是只有 XML 格式

----- 5xx 服务器端错误,服务器未能实现合法的请求
500 Internal Server Error - [*] 服务器发生错误,用户将无法判断发出的请求是否成功

7. 错误处理

如果状态码不是 2xx 就应该向用户返回出错信息,可以采用如下格式。

{
    "code": 0,               # 错误码
	"message": "success",
	"total": 10,
	"data": [{
	}]
}

对于 HTTP 来说,可以根据 code 映射到对应的 HTTP Code 值。

8. HTTP 头

使用 HTTP 头声明序列化格式,在客户端和服务端,双方都要知道通讯的格式,格式在 HTTP 头部中指定。

Content-Type    请求格式
Accept          系列可接受的响应格式

参考

  • Tools 各种 OpenAPI 工具集,以及一个不错的前端管理 redoc,而 Swagger 使用最多。