阅读 295

RESTful 架构

author githubId: golone

前言

看了两天 RESTful 总结的笔记,第一次在掘金发文章,若有谬误还请大佬们海涵。

概念

  1. RPC 面向方法
  2. SOA 面向消息
  3. REST 面向资源

REST:

REST(Representational State Transfer)是种互联网软件架构模式,由 Roy Fielding 在他 2000 年的博士论文《架构风格与基于网络的软件架构设计》(译)中提出,REST 一经提出就流行起来, 迅速取代了复杂笨重的 SOAP。

要理解 REST,最好是认真理解 Representational State Transfer 是什么,它的直译是“表现层状态转化”,其中省略了主语,完整的意思应该是“资源的表现层状态转化”,关键词是“资源”、“表现层”、“状态转化”。

资源:

“资源”就是网络上的一个信息实体,REST 规定用一个 URI(统一资源定位符)来唯一标识一个资源,通过 URI 可以获取到任意资源。

表现层:

“资源”只是信息,它可以有多种存在方式(比如一段单纯的文本可以被表示为 txt、HTML、XML、JSON,甚至二进制等多种格式),我们把资源所处的存在方式称为它的“表现层”, 可以把“表现层”理解为资源的“视图”,在 REST 里,URI 只用来标识资源,并不标识它处于哪个表现层,它的表现层是在 HTTP header 中由 Accept、Content-Type 指定的,这两个字段用于标识资源当前所处 的“表现层”。

状态转化:

“状态”包括资源本身信息和它所处的表现层两个方面(这个概念可能是我造的,我查的资料里并没有把“状态”作为一个单独的概念过,但我觉得应该加这么个概念,不然逻辑上有些不通), “状态转化”是指资源从一种“状态”改变到另一种“状态”,这个“改变”可能是表现层的改变,也可能是资源本身信息的改变,访问接口,就是通知服务端进行“状态转化”, 比如 CURD:

操作 影响
信息改变,多了一些信息。
信息改变,少了一些信息。
信息改变,变了一些信息。
表现层改变,数据被从数据库中取出转为 html、json 等。

REST 规定用 HTTP method 来作为告知服务端进行“状态转化”的手段,具体的说就是用 HTTP 的不同请求方法来指定不同的操作,比如常见的 GET、POST、PUT、DELETE 分别对应 CURD, 告知服务端进行增删改查四种状态转化。

以上是 REST 的核心概念,严格来说它有以下几条约束:

  1. 每个资源都拥有一个唯一的资源标识。
  2. 消息的自描述性。
  3. 资源的自描述性。

RESTful:

如果一个架构符合 REST 的规范,就称它为 RESTful 架构,采用 RESTful 架构的 web 服务也被称为 Restful web service。

在 Restful web service 中,每个 url 代表一种资源(resource),所以 url 中不能有动词,只能有名词,甚至所用的名词往往与数据表名相对应,而数据表通常包含不止一条记录, 所以 url 中的名词通常用复数。

补充:

这些是我参考的资料中,我觉得有趣或专业的几篇:

  1. 一篇很有趣的讲 RESTful api 的文章:如何给老婆解释什么是 RESTful
  2. REST 的局限性
  3. 如果你想知道 REST 是怎么来的:推导 REST

相比 REST,api 设计在 2015 年又出了套更有革命意义的理论 GraphQL,基本思路是在前、后端间放一个中间服务器,然后定义一种查询语言让前端描述所需数据,中间服务器接收前端发来的查询进行解析, 然后自动请求对应的接口并整理数据后再返回给前端,好处是前端获取数据时不用请求多个接口,一次搞定,也不用记接口路由、参数,这些东西前端都不用管,只管查就是了, 说起来就是个类似 sql 的东西,很方便,详情可以看这两篇文章:REST 将会过时, 而 GraphQL 则会长存REST 2.0 在此,它的名字叫 GraphQL

URL 设计

RPC 以动词为中心,RESTful 以名词为中心。

加入新功能时,以动词为中心往往必须添加新的动词,并且后端要实现该动词,前端要知道这个新的动词并进行调用; 而以名词为中心则没这么麻烦,无论要加什么新功能, 只要没有添加新的资源,url 是不变的,前后端只需关注各自实现即可,不需要在接口上分心。以名词为中心设计 RESTful 风格的接口时,应注意以下几点:

  1. 单、复数,这很好理解,根据资源的数量确定用哪种形式的名词,不赘述了。
  2. 资源等级,资源分主资源、子资源,主资源代表一类资源,所以主资源应放在 url 中间而不是末尾,例如 /users/friends,users 代表所有用户,friends 代表该用户的所有好友。
  3. query、params,RESTful 比较提倡 params 方式传参,例如将 /v1/users?id=x 写成 /v1/users/:id,参数放在路径中,全部这样做显然不现实,比如文件上传、 存在多个可选参数等,但除这些之外,应尽量用 params 方式传参。
  4. HTTP 方法,RESTful 不允许在 url 中使用动词,而 HTTP1.1 刚好定义了 8 个方法指代 8 个动词,所以 RESTful 提倡用 http 提供的方法来描述常见操作,例如 用 put、post、get、delete 来分别对应资源的 CURD。

参数:

既然 url 代表资源,那么进一步,可不可以让 url 更接近 sql?这样可以使接口变得更灵活,基于这点,RESTful api 有了以下这些常见参数:

字段 作用
limit 指定返回记录的数量。
offset 指定返回记录的开始位置。
page_number、page_size 指定第几页,以及每页的记录数。
orderBy、order 指定返回结果按照哪个属性排序,以及排序顺序。
where 直接指定 where 条件。
id 指定记录 id。

PS:有些 client 只支持 GET、POST,因此必须由服务端来模拟其它方法,这可以通过给请求的 header 加上 X-HTTP-Method-Override 字段来实现,它会告诉服务端应该用哪个方法,例如:

        POST /api/Person/4 HTTP/1.1
        X-HTTP-Method-Override: PUT

这样,X-HTTP-Method-Override 就指定该次请求的方法是 PUT,而不是 POST。

响应报文

状态码:

为了保证消息的自描述性,应该使用以下状态码来表示常见情况:

code 含义
200(OK) 请求成功,没毛病。
204(无内容) 请求成功,但资源为空。
301(Moved Permanently) 当前资源 URI 已变更。
400(bad request) 无效请求(如参数错误)
404(not found) 资源不存在。
500(internal server error) 服务器内部错误。
503(Service Unavailable) 服务端暂无法处理请求。

不要返回纯本文:

接口返回的数据应是 JSON 格式,相应的 response header 的 Content-Type 要设为 application/json。前端请求时,也应明确告诉服务器接受 JSON 格式,即 request header 的 ACCEPT 要设为 application/json。JSON 格式并不是 RESTful 要求的,只是当下流行的数据传输格式,类似不成文规定,以后可能会变。(2019-11-23

提供链接:

API 的使用者未必知道 URL 是如何设计的,因此 RESTful api 还要求资源自描述性。阮一峰博客里记载的一个解决方法是,在响应数据中给出相关链接。 这样用户只要记住一个 URL,就可以发现其他 URL。这种方法叫做 HATEOAS。比如 GitHub 的 API 都记载在 api.github.com ,访问它,就可以得到所有 URL, 或者访问其中一个 URL,返回与其相关的 URL。这样,用户不需要记住 URL 设计,只要从 api.github.com 一步步查就可以了。

个人看法

RESTful api 可以很好的实现一套接口,多端使用,非常优秀,但完全按照它的标准来做我觉得不行,譬如 url 中不能包含动词这点,动词单纯由 http 方法来提供未免不够用,有时 api 的作用并不只是对 资源进行 CURD 四个操作,这就需要更多地动词,所以我觉得必要时在 url 内加个动词是很正常的。(而且我还是喜欢只用 get、post,不想搞那么多幺蛾子方法,加个动词又不会死 。。。

另外是参数问题,我看过的技术博客在列举 RESTful api 时普遍有类似 /users/123/articles/54 的例子,我觉得改为 /users/articles/123/54 才是 更合适的做法,这样后端在定义路由时就可以写为 /users/articles/:userId/:articleId,先路径,再参数,集中存放,比较舒服。

关注下面的标签,发现更多相似文章
评论