REST架构风格研究工作生活

RESTful 接口规范

2019-07-04  本文已影响2人  nikytwo

总方针

构建易于理解和使用的RESTful接口。

接口应该是直观的,调用者可以通过接口来获得系统或应用程序中所有业务服务的工作节奏。

一般来说,可以使用以下的指导方针来进行接口的设计。

  1. 使用标准HTTP动词--围绕这些HTTP动词(GET/PUT/POST/PATCHDELETE)对基本的行为进行建模。
  2. 使用URI来传达意图--使用URI来描述问题域中的不同资源,并为问题域内的资源的关系提供一种基本机制。
  3. 使用JSON进行响应--JSON是一种轻量级的数据序列化协议。
  4. 使用HTTP状态码来传达结果--HTTP协议具有丰富的标准响应代码,来指示服务的成功和失败。学习这些状态码,并且,最重要的是,在所有接口中始终如一地使用它们。

所有这些指导方针都是为了完成一件事,那就是使接口易于理解和使用。
我们希望调用者坐下来查看一下接口就能开始使用它们。
如果接口不容易使用,开发人员就会另辟道路,破坏架构的意图。

资源与URI

REST全称是 Representational State Transfer (表述性状态转移)。其中表述指的就是资源。

URI既可看成是资源的地址,也可以看成是资源的名称。

URI的设计应该遵循可寻址性原则,具有自描述性,需要在形式上给人以直觉的关联。

URIHTTP动词作用的对象。它应该只有名词,不能包含动词。

URI的设计应该注意:

  1. URI中不能有动词: 动词应该由HTTP的动作(GET/POST/PUT/PATCH/DELETE等)来表示
  2. URI结尾不应该包含斜杠“/”
  3. 正斜杠分隔符“/”必须用来指示层级关系
  4. 应该使用连接符“-”来提高URI的可读性:浏览器默认会给超链接加上下划线,因此不要用其做URI分隔符
  5. URI路径首选小写字母:RFC-3986URI定义为区分大小写,但URI中的scheme(协议名)和host(主机名)除外
  6. URI路径中的名词建议使用复数
  7. 避免层级过深的URI(太多的层级在另一个侧面反应该接口有太多的职责)

资源操作

HTTP 通常有以下5种动词:

根据 HTTP 规范,动词一律大写。

资源过滤

很多情况,资源会有多级分类,因此很容易写出多级的URI,比如某个作者的某一类文章(/authors/123/categories/2)。

这种URI不易于扩展,语义也不明确,不能直观表达其含义。

更好的做法是,将次要的级别用查询字符串进行表达。如:

/authors/123?category=2
/articles?published=true

同样的,通过使用查询字符串,实现排序、投影和分页。

与之相反

经常使用的、复杂的查询可以标签化。
如:

/authors/123?status=close&sort=created,desc

可转化为:

/authors/123/closed
// 或者
/authors/123#closed

返回状态码

HTTP状态码为三位数,分五类:

HTTP包含了100多个状态码,覆盖了大多数可能遇到的情况。
每一种状态都有标准的(或约定的)解释,客户端只需查看状态,就可以大致判断发生了什么情况。
所以服务器应该尽可能使用这些标准的HTTP状态码,来表达执行结果状态。

通常不需要1**这一类状态码。
以下是常用的:

返回内容

返回内容数据格式应该是结构化的(如:一个JSON对象)。

客户端请求时也要明确告诉服务器,可以接受的格式。

错误处理

错误时不要返回200状态码。
因为只有解析数据体后,才能得知操作失败。而且与HTTP状态码描述冲突。

假如你不利用HTTP状态码丰富的应用语义,那么你就错失了提高重用性、增强互操作性和提升松耦合性的机会。

这些所谓的RESTful应用必须通过响应体才能给出错误信息,那么这个跟SOAP没什么区别。

正确的做法是,状态码反映发生的错误,而具体的错误信息放在数据体中。
如:

HTTP/1.1 400 Bab Request
Content-Type: application/json

{
    "error": "Invalid param."
    "data": {
        "name": "This field is required."
    }
}

另外建议要区分业务异常和非业务异常。
业务异常的返回4**的状态码,非业务异常的返回500状态码。

资源的表述

客户端通过HTTP方法获取的不是资源本身,而是资源的一种表述而已。
资源在外界的具体呈现,可以有多种表述形式,如:html、xml、json、png、jpg等。

资源的表述包括数据和描述数据的元数据,如:HTTP头中的Content-Type就是一个元数据属性。

所以应该通过HTTP的内容协商,来获取资源的表述。

如:客户端可以通过Accept头请求一种特定格式的表述,服务器则通过Content-Type告诉客户端资源的表述形式。

区分格式

应该优先使用内容协商来区分表述格式。

使用后缀表示格式,无疑是直观的,但它混淆了资源的名称和资源的表述形式。

超媒体(Hypermedia)

“超媒体即应用状态引擎(hypermedia as the engine of application state)”。

当浏览Web网页时,我们从一个链接跳到一个页面,再从页面里的另一个链接跳到另一个页面,这就是在用超媒体的概念:把一个个资源链接起来。

要达到这个目的,就要求在资源的表述里加上链接来引导客户端。

如 GitHub api 中的分页,是在头信息的Link提供:

Link: <https://api.github.com/search/code?q=addClass+user%3Amozilla&page=15>; rel="next",
  <https://api.github.com/search/code?q=addClass+user%3Amozilla&page=34>; rel="last",
  <https://api.github.com/search/code?q=addClass+user%3Amozilla&page=1>; rel="first",
  <https://api.github.com/search/code?q=addClass+user%3Amozilla&page=13>; rel="prev"

应该多花时间来给资源的表述提供链接,而不是专注于寻找漂亮的URI

速率限制

响应头建议包含当前限流状态

如 GitHub api 中使用3个相关的头信息进行说明:

建议同时提供可以不影响RateLimit的请求接口,来查询当前的RateLimit

无状态

RESTful应该是无状态通信的。
服务端不应该保存客户端(应用)状态。

客户端与服务端交互必须是无状态的,并在每次请求中包含处理所需的一切信息。

这种无状态通信,使得服务端能够理解独立的请求和响应。
在多次请求中,同一客户端也不再需要依赖同一服务器,方便实现高可扩展和高可用性的服务端。

服务端通过超媒体告诉客户端当前(应用)状态可以有哪些后续的状态。
这些类似“下一页”的链接将指引客户端如何从当前状态进入下一个可能的状态。

版本

三种方式:

  1. URI中:/api/v1/**
  2. Accept Header: Accept: application/json+v1
  3. 自定义Header: X-Api-Version: 1

建议第一种,虽然没那么优雅,但最明显方便。

另一种观点:一个资源,应只有一个单一的URI来标示,资源版本不应该体现在URI中。

以上见仁见智,不强制要求。

其他(Header)头信息的使用

其他

1. 动词的覆盖

有些客户端仅支持GETPOST两种方法。那么,服务器必须可以接受通过POST模拟其他方法(PUTPACTHDELETE)。

客户端在发送HTTP请求时,要加上X-HTTP-Method-Override头信息,告诉服务器应该使用那个动词,覆盖POST方法。

2. 提供相关链接

服务接口的使用者未必知道接口有那些,以及它的相关服务。
好的接口,应该在相应中给出相关链接,以便于下一步操作。
这样,用户就可以发现其他接口的URI
这种方法叫HATEOAS
如 GitHub 的 API 都在api.github.com这个域名。

参考

上一篇下一篇

猜你喜欢

热点阅读