We FALL ASleep At Night, We Do REST Right

Github 同步发表连接html

前言

笔者在上一篇文章中提过,任何一种非“强制性”约束同时也没有“标杆”工具支持的开发风格或协议,最后都会在不一样的程序员手中获得不一样的诠释,微服务是如此,DDD 是如此,笔者把它称为技术思想上的“康威定律”。不出意外的,REST 一样难逃此劫。光是在学习和收集资料的过程当中,笔者就已经见过不下十多篇此类理解,甚至于在 url 中使用短划线或下划线链接单词也是众口难调。git

尽管这只是小事。程序员

微软也发布过关于如何设计 REST API 的开发指南,可是不幸的是,REST 的创始人 Roy Fielding 认为微软的 REST API 规范与 REST 没有多大关系。github

“即便是我最糟糕的 REST 描述也比微软的 API 指南提供的总结或参考要好不少。”后端

那什么才是正确的 REST 描述呢,或者说,REST 是什么。本文的创做动机即是希冀于解决这样一个问题。api

本文假设读者已经具有基本的 REST 和 Web 知识,哪怕大家如今认为 HTTP API 就是 REST API 也可。浏览器

REST 起源

REST 英文全称为 Representational State Transfer,又名“表述性状态移交”,是由 Roy Fielding 在《架构风格与基于网络的软件架构设计》一文中提出的一种架构风格(Architectural Style)。而在这篇 REST 圣经问世以前,R.F 博士就已经参与了 HTTP 1.0 协议规范的开发工做(1996年),而且负责了 HTTP 1.1 协议规范的制定(1997年)。缓存

一种架构风格由一组准确命名的,相互协做的架构约束组成。当咱们在谈论 REST 本质的时候,咱们谈论的实际上是架构约束。安全

REST 用以指导基于网络的分布式超媒体系统的设计和实现,Web(即万维网)就是一种典型的分布式超媒体系统。能够肯定的是,在制定 HTTP 协议的过程当中,R.F 博士就已经以 REST 架构风格做为指导原则来完成相关工做。论文中提到了如下内容:ruby

“在过去的6年中,咱们使用 REST 架构风格来指导现代 Web 架构的设计和开发。这个工做是与我所创做的 HTTP 和 URI 两个互联网规范共同完成的,这两个规范定义了在 Web 上进行交互的全部组件所使用的通用接口。”

“自从1994年起,REST 架构风格就被用来指导现代 Web 架构的设计与开发。”

“开发 REST 的动机是为 Web 的运转方式建立一种架构模型,使之成为 Web 协议标准的指导框架。”

“REST 的初版开发于1994年10月至1995年8月之间,起初,在我编写 HTTP/1.0 规范和最初的 HTTP/1.1 建议时,将它用来做为表达各类 Web 概念的一种方法……”

Web 架构规范主要包括 HTTP, URI 和 HTML 等。

因此咱们也不难理解为何 REST 与 Web 和 HTTP 可以结合得如此紧密。尽管直到2000年,这只“鸡”才在下完鸡蛋后,出如今了世人面前。

REST 演化

REST 约束

不管是否愿意认可,REST 一开始就是为 Web 而服务的,能够这么说的是,REST 是现代 Web 的架构风格,Web 也是 REST 最典型和最成功的案例。包括在 R.F 博士的论文中,他也是在解决现代 Web 需求(没法控制的可伸缩性和独立部署)的过程当中而逐步推导出 REST。前文已经提到一种架构风格是由一组准确命名的,相互协做的架构约束组成。而所谓架构约束,即是这个推导过程当中最重要的产物。甚至高于 REST 自己。

早先的 Web 与 REST 所描述的模型有着大量出入,然而正是在对应的 HTTP 和 URI 规范出炉后,才有了所谓“现代 Web”的说法。笔者更愿意把“现代 Web”的定义期限定为1996年后。

客户端 - 服务端

设计与实现上的关注点分离。

无状态

在客户端没有发起请求时,服务器并不知道它的存在。一样的,服务器无须维护当前请求以外的客户端状态,从而改善服务器的可伸缩性。Session 和 Cookie 都是“须要”被抛弃的。
若是有些应用状态重要到服务器须要去关心,那它应该成为一个资源。

缓存

对于客户端而言,使用缓存则是维护状态和提高性能的更好作法。

统一接口

使 REST 架构风格区别于其余基于网络的架构风格的核心特征是,它强调组件之间具备一个统一的接口。实现与他们所提供的服务是解耦的,这促进了独立的可进化性。同时这也引伸出了其余的约束:资源识别;经过表述来操做资源;自描述信息;超媒体做为应用状态引擎(即 HATEOAS)。下文会专门说明。

分层系统

“分层系统”约束在“客户 - 服务端”约束的基础上增长了代理组件和网关组件。尽管笔者认为代理和网关都不是重点,“分层系统”约束更注重的是“在客户端和服务端之间添加一个组件应该是一个透明操做”,组件只能“看到”与其交互的相邻层(是否是想到了迪米特法则),使用层级来封装服务,同时可以支持负载均衡和诸如安全性检查的功能。

按需代码

这是六大约束中惟一的可选约束。REST 容许客户端经过下载并执行脚本或其余形式的代码,对客户端的功能进行扩展,从而提升客户端的灵活性和性能。通俗点说,HTML 中的 <script> 标签就是一种按需代码,尽管它可能会致使一些例如跨站脚本攻击这样的问题。

统一接口约束

R.F 博士在论文中针对六大约束中的“统一接口”作了额外的约束分解和说明,但遗憾的是并无以列表的方式展现出来。但在接下来的内容中你可能就会发现,这几项多是目前大部分开发者践行 REST 原则时所遵循的所有标准。

资源识别

REST 对于信息的核心抽象是资源,任何可以被命名的信息均可以称为是资源,只要你的想象力容许。资源一词一般和“可寻址性”绑定,一个或多个 URI 标识一个资源。若是资源的 URI 发生了变化,服务器应该使用超媒体引导客户端访问新的 URI 或提示对应信息。

经过表述来操做资源

当客户端对一个资源发起一个请求时,服务器会以一种有效的方式提供一个采集了资源信息的文档做为回应。这就是表述——一种以机器可读的方式对资源当前状态的说明。客户端和服务器之间也能够继续传递表述,从而对资源执行某种操做。客户端历来不会直接看到资源,能看到的都是资源的表述。能够这么说的是,服务器发送的表述用于描述资源当前的状态,客户端发送的表述用于描述客户端但愿资源拥有的状态,这就是表述性状态转移/移交。

一个表述由一个“字节序列”和描述这些字节的“表述元数据”构成,且不与服务器端代码绑定,这意味着当服务器端的资源实现和业务操做代码发生变化时,能够选择不更改资源的呈现方式。

值得注意的是,通常人一般会将表述认为成资源的“值”,这虽然能够理解,可是当你请求一个天气服务时,千万不要认为表述必定即是温度等肯定的值信息,由于它仍然多是某次响应中的错误提示。一个表述的具体含义取决于消息中的控制数据。

“控制数据定义了在组件之间移交的消息的用途,例如被请求的动做或相应的含义。它也可用于提供请求的参数,或覆盖某些链接元素的默认行为。例如,可使用(包含在请求或响应消息中的)控制数据来修改缓存的行为。”

“表述的数据格式称为媒体类型(media type)。发送者可以将一个表述包含在一个消息中,发送给接收者。接收者收到消息以后,根据消息中的控制数据和媒体类型的性质,来对该消息进行处理。”

表述在现代 Web 中的实例包括 HTML,Json,XML,图片等。

自描述的消息

一个 (HTTP)消息体包含了全部足以让接收者理解它的必要信息,在现代 Web 中,自描述的消息由一些标准的HTTP方法、可定制的HTTP头信息、可定制的HTTP响应代码组成。扩展开来,它一般有如下三方面的含义。

  1. 请求之间的交互是无状态的。 对应于 REST 约束中的“无状态”约束,服务器能够独立处理每一个请求,而无须对该客户端先前全部请求的处理进行记忆。
  2. 使用标准的方法和媒体类型来表达语义和交换信息。想一想 HTTP Methods 和 HTTP Headers,客户端一般靠这些信息理解请求的含义和解析消息体。
  3. 响应能够明确地代表其可缓存性。

超媒体做为应用状态引擎

该约束即是大名鼎鼎的“HATEOAS”(Hypertext/Hypermedia As The Engine Of Application State),但实际上 R.F 博士在论文中并无对它作过详细的介绍。在目前的共识中(讽刺的是在大多数时候它并无被应用到设计所谓 REST APIs 中去),HATEOAS 意味着客户端应该使用超文原本做为你在接收到当前的表述后,再进行下一步寻址的方式。更进一步的,客户端须要经过解析超文本理解服务器提供了哪些资源,而不是在客户端事先定义或约定俗成。

“客户端依赖的是超文本的状态迁移语义,而不该该对因而否存在某个URI或URI的某种特殊构造方式做出假设。一切都有可能变化,只有超媒体的状态迁移语义可以长期保持稳定。” —— 《理解本真的REST架构风格》

最终结果即是客户端能够自动化地适应服务器端的变化,服务器也容许在不破坏全部客户端的状况下更改它底层的实现。一样的,咱们能够列出几点说明。

  1. 全部的应用状态维持在客户端一侧。改变应用状态是客户端的职责。
  2. 客户端仅可以经过发送请求和处理响应来改变应用状态。
  3. 客户端能够经过已经收到的表述中的超文本知道接下来能够操做的请求动做(如 HTML 中的超连接)。
  4. 超文本是应用状态变化背后的动力。

看起来,上述四点内容说的可能是集中式 Web 应用的状况,在现在多用 Web APIs 进行先后端分离开发的 Web 应用中,HATEOAS 又该作如何理解呢?现在有这么一项技术可让超文本继续充当驱动应用状态更新流动的引擎,那就是 Web Links,RFC 5988 定义了 HTTP 的这项扩展。

Github REST API v3 中,咱们能够在不少 apis (如列表翻页)的响应体中看到 Link Header,对应引导的 Uri 一样有相关标准,即 Uri Templates(RFC 6570)

Link: <https://api.github.com/user/repos?page=3&per_page=100>; rel="next",
  <https://api.github.com/user/repos?page=50&per_page=100>; rel="last"

至此,想必你也大体清晰了 HATEOAS 的含义。

若是能够的话,你能够将“应用状态”理解为客户端对资源操做后的展现结果。好比“主页”,“博客”,“关于我”,“成功提交”等操做界面。它和“资源状态”有抽象概念上的区分。


你能够放弃对 Hypertext 和 Hypermedia 之间区别的思考,笔者也认为它们在你理解 REST 时并不该该区分。

Richardson 成熟度模型

Richardson Maturity Model 是一个能够尝试的甜品,特别是当你在设计本身的 REST APIs 时。和 REST 类似,你也能够把该模型称为指导原则。

笔者无心去解释这几个层级,由于这些层级和“统一接口”的扩展约束都是间接的映射关系。并且正如上文一直在强调的,REST 不依赖于任何单一的通讯/传输/移交协议,因此模型中的 HTTP 指示就有些回味无穷了。

“它是标准吗?”
“固然不是,它只是目前设计 REST APIs 时的一种潜规则。”

总结

对于理解概念性文章的总结老是特别艰难,看起来内容挺丰富,同时结合了 R.F 博士论文及其译者李琨教授相关文章,其实摊开了目录整篇文章也就只涉及到了起源—>约束->模型这样几个方面,实际上 REST 也确实只是一组约束而已。最后,仅用笔者认为的 R.F 博士论文中相当重要的两段话做为结束。

“所以,REST的模型应用是一个引擎,它经过检查和选择当前的表述集合中的状态迁移选项,从一个状态迁移到下一个状态。绝不奇怪,这与一个超媒体浏览器的用户接口彻底匹配。然而,REST风格并不假设全部应用都是浏览器。事实上,通用的链接器接口对服务器隐藏了应用的细节,所以各类形式的用户代理都是等价的,不管是为一个索引服务执行信息获取任务的自动化机器人,仍是查找匹配特定查询标准的数据的私人代理,或者是忙于巡视破损的引用或被修改的内容的维护爬虫。”

“这个名称“表述性状态转移”是有意唤起人们对于一个良好设计的Web应用如何运转的印象:一个由网页组成的网络(一个虚拟状态机),用户经过选择连接(状态迁移)在应用中前进,致使下一个页面(表明应用的下一个状态)被转移给用户,而且呈现给他们,以便他们来使用。”

有效的参考文档

  1. 理解本真的REST架构风格
  2. 理解本质的 REST
  3. REST APIs must be hypertext-driven
  4. HATEOAS做为领域特定协议描述的引擎
  5. Richardson Maturity Model
相关文章
相关标签/搜索