在 Web 开发中,没有哪个话题比表述性状态转移 (REST) 更能引起混淆。这个术语来自 第 5 章,出自 Roy Fielding 在 加州大学欧文分校 的博士论文。
在这篇文章中,我们将探讨这篇论文的第 5 章,并为非学术性的 Web 开发人员总结重要的概念。这篇论文内容丰富,涉及大量专业术语,对于那些没有学术背景、不感兴趣进行正式博士论文写作的人来说并不相关。
在读完这篇文章后,你应该能够更好地理解 REST,特别是 统一接口 的概念。
首先要理解的是,REST 是对**原始 Web** 的描述。Fielding 将 REST 描述为“分布式超媒体系统的架构风格”,听起来很复杂,但实际上就是我们都熟悉和喜爱的 Web:点击超链接、提交表单、查看图片、阅读段落以及所有其他内容。
它**不是**为描述 JSON API 的特定方法而创建的,尽管如今大多数人是在 JSON API 的背景下听到 REST 的。Fielding 描述的是早期 Web,特别是它与早期客户端/服务器架构的不同之处。
不幸的是,对于非学术性读者来说,Fielding 在 第 5.1 节 中采用了从第一性原理推导出 REST 的方法。在这里,我将总结每个部分,并在重要的部分进行澄清和补充说明。
REST 当然是一种客户端-服务器架构,因为 Web 本身就是客户端 (浏览器) 和服务器 (HTTP 服务器) 系统。
大多数开发者都知道,Web 旨在是无状态的。所有请求都应该包含理解该请求所需的所有信息。例如,不应该隐式地将一个长期运行的事务与一系列请求关联起来,就像你使用 SQL 数据库会话时可能遇到的那样。
你可能知道,HTTP 中内置了 缓存机制。你现在不需要了解它的细节,但可以以后再深入研究。
在我看来,这一部分是 REST 架构的核心,不幸的是它非常简短,所以我们将花一些时间扩展它,而不仅仅是总结它。本章开头写道:
将 REST 架构风格与其他基于网络的风格区分开来的核心特征在于它强调组件之间的统一接口
为了澄清关于统一接口究竟是什么的讨论,让我们考虑一些简单的 HTML,我希望所有阅读本文的人都能理解。
<html>
<body>
<section>
<p>
Name: Joe Blow
</p>
<p>
Email: joe@blow.com
</p>
<p>
<a href="/contacts/42/edit">Edit</a>
<a href="/contacts/42/email">Email</a>
<a href="/contacts/42/archive">Archive</a>
</p>
</section>
</body>
</html>
这里有一段基本的 HTML 代码,包含一些 div、一些信息以及一些锚标签,用于对联系人执行各种操作。没什么特别的。再次强调,为了讨论的方便,想象一下这些内容可以在 http://example.com/contacts/42 中找到。
回到博士论文。
REST 由四项接口约束定义:资源的标识;通过表示对资源进行操作;自描述消息;以及超媒体作为应用程序状态的引擎。
让我们依次讲解每个约束。
REST 的第一个方面是**资源**的概念,这些资源可以通过…… 嗯,通用资源定位器 (URL) 找到。请注意,HTML 包含用于对该资源 (contacts/42
) 执行操作的附加 URL,遵循 URL 路径的传统层次结构安排。
听起来很复杂,但它只是意味着你可以通过各种表示 (即 HTML 页面) 来更新和修改资源 (即联系人),而不是必须发出例如 SQL 命令来修改它。
这是一个关键概念。请注意,浏览器是客户端-服务器设置中的客户端,**它对联系人一无所知**。然而,它能够简单地通过渲染服务器返回的 HTML 来渲染一个“联系人 UI”。消息本身是完全自描述的,包含客户端关于数据和对该数据可能执行的操作 (以链接的形式) 的所有信息。
现在,将它与相同数据的 JSON 表示进行对比。
{
"name" : "Joe Blow",
"email" : "joe@example.com"
}
显然,这更小,但使用此数据的客户端必须决定两件关键的事情。
第一部分通常通过客户端模板完成。第二部分通常通过阅读 API 文档并将与服务器的交互直接编码到客户端中来完成。
这就是 RESTful 系统和传统客户端-服务器系统之间区别的关键所在:在 RESTful 系统中,客户端 (即浏览器) 对资源一无所知,它只知道如何渲染超媒体。在客户端-服务器系统中,关于资源的知识嵌入到客户端中。
这两种方法各有优缺点,但是 RESTful 方法 (以早期 Web 的形式) 被证明是极其可靠和灵活的。它将关于资源的大量知识隐藏在 HTML 的**统一接口**背后,因此客户端没有机会像厚客户端那样出现故障。
现在,你可能已经注意到,在过去十年中,Web 开发已经从 RESTful 架构转向更传统的客户端-服务器设置,使用 JSON API。你可能还注意到,关于 API 版本控制、提供更通用的查询功能等的讨论和问题越来越多。这并非偶然:当我们将浏览器变成用于托管厚客户端应用程序的虚拟机时,我们正在失去 RESTful 模型的灵活性。
最后一个概念与前一个概念相呼应:客户端通过与超媒体本身 (通过表单和链接) 中找到的 URL 交互来转换应用程序状态。因此,在上面的 HTML 示例中,编辑、电子邮件和归档联系人的功能都编码为 HTML 中的锚点。如果其中一项操作不可用,或者一项新的操作变得可用,它将在页面刷新后出现在新的 HTML 代码中。
这与厚客户端方法形成对比,在厚客户端方法中,例如,本地存储可能与后端异步同步,因此 HTML 不充当应用程序状态的引擎,而是充当 (有点笨拙的) UI 描述语言。
有趣的是,HATEOAS 的维基百科文章 使用了 JSON,而 JSON 不是自然的超媒体。如果你愿意,可以在 JSON 之上构建一些 RESTful 行为,但它在现实世界中很少有用,而且 HATEOAS 通常在 JSON API 中被忽略。这是有道理的,因为 JSON API 主要用于传统的客户端-服务器架构,不特别适合 RESTful 风格。
这就是 REST 的核心,也是本文的核心。你可以继续阅读以获得更多细节和对 Fielding 论文的分析,但这里的主要 takeaway 是 RESTful 超媒体架构与传统客户端-服务器架构之间存在着显著的区别,这种区别主要围绕**统一接口**的概念,特别是它们的自我描述性质。
再次强调,不要被这些术语困扰,只需思考一下这段 HTML 代码,以及它在灵活性与创造性方面的奇迹。
<html>
<body>
<div>
<div>
Name: Joe Blow
</div>
<div>
Email: joe@blow.com
</div>
<div>
<a href="/contacts/42/edit">Edit</a>
<a href="/contacts/42/email">Email</a>
<a href="/contacts/42/archive">Archive</a>
</div>
</div>
</body>
</html>
你不需要了解太多关于这一点的信息,只需要知道 CDN 存在,并且你应该使用它们。
同样,你不需要了解太多关于这一点的信息,只需要知道 Javascript 存在,并且它是唯一可选的部分。
我不会像其他部分那样深入探讨这部分内容,因为它会变得非常技术化,坦率地说,有点无聊和重复 (正如你对一篇博士论文所期望的那样)。这部分中的两个主要概念是资源和表示。
摘自论文:
REST 中信息的关键抽象是资源。任何可以命名的信息都可以是资源:文档或图像、时间服务 (例如“洛杉矶今天的天气”)、其他资源的集合、非虚拟对象 (例如一个人) 等等。
实际上,资源是任何可以通过 URL 寻址的东西。当访问一个 URL 时会发生什么?
嗯,你会得到该资源的**表示**,以 HTTP 响应的形式,其中可能包含 HTML、指令等等。
我在这部分中没有发现太多实际用途。有一些关于控制数据、媒体类型等等的内容,这些内容在需要时值得学习,但不是 Web 开发中常用的方面。
剩余的第 5.2 节同样对一般用户没有太多价值。
按照既定的模式,我觉得这部分对于普通 Web 开发人员来说没有太多有用的新信息,只有一个重大例外:它列出了 REST 的优势。
摘自论文:
REST 的客户端-服务器分离关注点简化了组件实现,降低了连接器语义的复杂性,提高了性能调优的有效性,并提高了纯服务器组件的可扩展性。分层系统约束允许在通信的不同点引入中间件(代理、网关和防火墙),而无需更改组件之间的接口,从而使它们能够协助通信转换或通过大规模共享缓存来提高性能。REST 通过将消息约束为自描述来实现中间处理:请求之间的交互是无状态的,使用标准方法和媒体类型来指示语义和交换信息,并且响应明确指示缓存能力。
这一切都是真的,这也是网络如此成功并将在未来继续成功的原因。
这些简短的章节与对 REST 感兴趣的非学术人士无关。
就是这样,对 Roy Fielding 博士论文第 5 章的简要介绍,该论文为我们带来了 REST 这个术语。我重点关注了我认为对于 Web 开发人员来说最重要的部分,并试图传达 REST 如何描述原始的 Web 模型。在我看来,统一接口概念是 REST 最重要和最有趣的方面,对于 Web 开发人员来说理解它非常有用,因为它主要负责上面描述的优势。
最后,我希望你能明白为什么 REST 不适合描述当今使用的大多数 JSON API。