认识 RESTful

REST 称为表现层状态转化(或者称为具象状态传输)。

在前后端分离的应用模式里,后端 API 接口如何定义?例如对于后端数据库中保存了商品的信息,前端可能需要对商品数据进行增删改查,那相应的每个操作后端都需要提供一个 API 接口:

  • POST /add-goods 增加商品
  • POST /delete-goods 删除商品
  • POST /update-goods 修改商品
  • GET /get-goods 查询商品信息

对于接口的请求方式与路径,每个后端开发人员可能都有自己的定义方式,风格迥异。API 的 RESTful 设计风格,被广大开发人员接受并使用。

RESTful 的内容

RESTful 是一种开发理念。

RESTful 风格与非 RESTful 风格的区别

RESTful 的特点:URL 简洁,将参数通过 URL 传到服务器。

什么是 RESTful 架构

要理解 RESTful 架构,需要理解 Representational State Transfer 这三个单词的意思。

如果一个架构符合 REST 原则,就称它为 RESTful 架构。

  • 具象(Representational):就是指表现层,要表现的对象也就是”资源”。什么是资源呢?网站就是资源共享的东西,客户端(浏览器)访问 Web 服务器,所获取的就叫资源,比如 HTML、txt、JSON、图片、视频等
  • 表现(State):比如文本可以用 txt 格式表现,也可以用 HTML 格式、XML 格式、JSON 格式表现,甚至可以采用二进制格式;图片可以用 JPG 格式表现,也可以用 PNG 格式表现

浏览器通过 URL 确定一个资源,但是如何确定它的具体表现形式呢?应该在 HTTP 请求的头信息中用 Accept 和 Content-Type 字段指定,这两个字段才是对”表现层”的描述。

  • 状态转换(Transfer):就是客户端和服务器互动的一个过程,在这个过程中,势必涉及到数据和状态的变化,这种变化叫做状态转换

互联网通信协议 HTTP 协议,客户端访问必然使用 HTTP 协议,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生”状态转化”(State Transfer)。

HTTP 协议实际上含有 4 个表示操作方式的动词,分别是 GET、POST、PUT、DELETE,它们分别对应四种操作。

而且 HTTP 协议是一种无状态协议,这样就必须把所有的状态都保存在服务器端。 因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生”状态转化”(State Transfer)。

RESTful 架构总结

综合上面的解释,RESTful 架构就是:

  1. 每一个 URL 代表一种资源
  2. 客户端和服务器之间,传递这种资源的某种表现层
  3. 客户端通过四个 HTTP 动词,对服务器端资源进行操作,实现”表现层状态转化”

设计方法

应该从以下几个方面去思考设计:

  1. 域名
  2. 版本(Versioning)
  3. 路径(Endpoint)
  4. HTTP 动词
  5. 过滤信息(Filtering)
  6. 状态码(Status Codes)
  7. 错误处理(Error handling)
  8. 返回结果
  9. 超媒体(Hypermedia API)

1. 域名

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

1
https://api.example.com

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

1
https://example.org/api/

2. 版本(Versioning)

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

1
2
3
http://www.example.com/app/1.0/foo
http://www.example.com/app/1.1/foo
http://www.example.com/app/2.0/foo

另一种做法是将版本号放在 HTTP 头信息中,但不如放入 URL 方便和直观。GitHub 采用这种做法。

因为不同的版本可以理解成同一种资源的不同表现形式,所以应该采用同一个 URL,版本号可以在 HTTP 请求头信息的 Accept 字段中进行区分:

1
2
3
Accept: vnd.example-com.foo+json; version=1.0
Accept: vnd.example-com.foo+json; version=1.1
Accept: vnd.example-com.foo+json; version=2.0

3. 路径(Endpoint)

路径又称”终点”(Endpoint),表示 API 的具体网址,每个网址代表一种资源(Resource)。

(1) 资源作为网址,只能有名词,不能有动词,而且所用的名词往往与数据库的表名对应。

(2) API 中的名词应该使用复数,无论子资源或者所有资源。

不好的例子:

1
2
3
/getProducts
/listOrders
/retreiveClientByOrder?orderId=1

对于一个简洁结构,应该始终用名词。此外,利用 HTTP 方法可以分离网址中的资源名称的操作:

1
2
3
4
GET /products    :返回所有产品清单
POST /products :新建产品到集合
GET /products/4 :获取产品 4
PUT /products/4 :更新产品 4

获取产品的 API 可以这样定义:

1
2
获取单个产品:GET /products/1
获取所有产品:GET /products

4. HTTP 动词

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

常用的 HTTP 动词(括号里是对应的 SQL 命令):

动词 说明 SQL 对应
GET 从服务器取出资源(一项或多项) SELECT
POST 在服务器新建一个资源 CREATE
PUT 在服务器更新资源(客户端提供改变后的完整资源) UPDATE
DELETE 从服务器删除资源 DELETE

不常用的 HTTP 动词:

动词 说明
PATCH 在服务器更新资源(客户端提供改变的属性)
HEAD 获取资源的元数据
OPTIONS 获取信息,关于资源的哪些属性是客户端可以改变的

示例:

1
2
3
4
5
6
7
8
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:删除某个指定动物园的指定动物

5. 过滤信息(Filtering)

如果记录数量很多,服务器不可能都将它们返回给用户。API 应该提供参数,过滤返回结果。

常见的参数:

1
2
3
4
5
?limit=10                    :指定返回记录的数量
?offset=10 :指定返回记录的开始位置
?page=2&per_page=100 :指定第几页,以及每页的记录数
?sortby=name&order=asc :指定返回结果按照哪个属性排序,以及排序顺序
?animal_type_id=1 :指定筛选条件

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

6. 状态码(Status Codes)

服务器向用户返回的状态码和提示信息,常见的有以下一些:

状态码 说明 适用动词
200 OK 服务器成功返回用户请求的数据 GET
201 CREATED 用户新建或修改数据成功 POST/PUT/PATCH
202 Accepted 一个请求已经进入后台排队(异步任务) *
204 NO CONTENT 用户删除数据成功 DELETE
400 INVALID REQUEST 用户发出的请求有错误,服务器没有进行新建或修改数据的操作 POST/PUT/PATCH
401 Unauthorized 用户没有权限(令牌、用户名、密码错误) *
403 Forbidden 用户得到授权,但是访问是被禁止的 *
404 NOT FOUND 用户发出的请求针对的是不存在的记录 *
406 Not Acceptable 用户请求的格式不可得 GET
410 Gone 用户请求的资源被永久删除,且不会再得到 GET
422 Unprocesable entity 创建一个对象时,发生一个验证错误 POST/PUT/PATCH
500 INTERNAL SERVER ERROR 服务器发生错误,用户将无法判断发出的请求是否成功 *

7. 错误处理(Error handling)

如果状态码是 4xx,服务器就应该向用户返回出错信息。一般来说,返回的信息中将 error 作为键名,出错信息作为键值即可:

1
2
3
{
"error": "Invalid API key"
}

8. 返回结果

针对不同操作,服务器向用户返回的结果应该符合以下规范:

操作 返回结果
GET /collection 返回资源对象的列表(数组)
GET /collection/resource 返回单个资源对象
POST /collection 返回新生成的资源对象
PUT /collection/resource 返回完整的资源对象
PATCH /collection/resource 返回完整的资源对象
DELETE /collection/resource 返回一个空文档

9. 超媒体(Hypermedia API)

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

比如 GitHub 的 API 就是这种设计,访问 api.github.com 会得到一个所有可用 API 的网址列表:

1
2
3
4
{
"current_user_url": "https://api.github.com/user",
"authorizations_url": "https://api.github.com/authorizations"
}

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

1
2
3
4
{
"message": "Requires authentication",
"documentation_url": "https://developer.github.com/v3"
}

服务器给出了提示信息,以及文档的网址。

提示

服务器返回的数据格式,应该尽量使用 JSON,避免使用 XML


本站由 sswfive 使用 Stellar 1.42.1 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

本站总访问量