REST 本身是一个高度抽象化的架构风格,因而总是很难对它有一个比较深入且印象深刻的理解。写这篇文章的目的,是自己对学习 REST 的一个总结,也希望可以通过这篇文章,能够让读者真正的理解 REST。
先来看看百度对 REST 的定义:
REST 即表述性状态传递(英文:Representational State Transfer,简称 REST)是 Roy Fielding 博士在 2000 年他的博士论文中提出来的一种软件架构风格。它是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。
在对 REST 更深一步的解释之前,我们先来看看 REST 的由来,而这对于 REST 的理解至关重要。
首先简单了解一下作者——Roy Thomas Fielding
一张图说明 REST 的由来:
好吧,这是一张很简陋的图,不过用来解释 REST 的由来足够了。 故事得从远古时期的 HTTP/1.0 协议说起,随着 web 技术的发展,沿用多年且面向静态文档的 HTTP/1.0 协议无法满足 web 应用的开发需求,作为 HTTP/1.0 协议专家组成员之一的 Roy Fielding 脱颖而出,成为了 HTTP/1.1 协议专家组的负责人,负责统筹制定新版本的协议。 Roy Fielding 和他的同事们在制定 HTTP/1.1 协议的过程中,从技术架构层面对 web 之所以能获得巨大成功做了一番深入的研究和总结,之后将这些总结纳入到一套理论框架之中,并利用这套理论框架中的指导原则,指导 HTTP/1.1 协议的设计方向。经过三年的修订,HTTP/1.1 协议于 1999 年 6 月正式成为规范。HTTP/1.1 协议设计取得了极大的成功,在发布之后的十年里,都没有多少人认为有修订的必要。 Fielding 在完成 HTTP/1.1 协议的设计工作之后,回到了加州大学欧文分校继续攻读自己的博士学位。第二年(2000 年)在他的博士学位论文 Architectural Styles and the Design of Network-based Software Architectures 中(中文版名为《架构风格与基于网络的软件架构设计》),Fielding 更为系统、严谨地阐述了这套理论框架,并且使用这套理论框架推导出了一种新的架构风格,并且为这种架构风格取了一个令人轻松愉快的名字“REST”——Representational State Transfer(表述性状态转移)的缩写。 这便是 REST 的由来,可以看出,REST 架构风格,是通过推导 web 的技术架构因素层面而总结出来的,总结出来的理论框架被用来指导 HTTP/1.1 协议的设计方向。那么我们可以这样理解,REST 是 Web 自身的架构风格,REST 是 HTTP/1.1 协议等 Web 规范的设计指导原则,HTTP/1.1 协议正是为实现 REST 风格的架构而设计的。
从 REST 的来源中我们发现,要想深刻理解 REST,首先得了解 web。 先来看一些 web 的相关知识:
百度百科 web(World Wide Web)即全球广域网,也称为万维网,它是一种基于超文本和 HTTP 的、全球性的、动态交互的、跨平台的分布式图形信息系统。是建立在 Internet 上的一种网络服务,为浏览者在 Internet 上查找和浏览信息提供了图形化的、易于访问的直观界面,其中的文档及超级链接将 Internet 上的信息节点组织成一个互为关联的网状结构。
维基百科 万维网(英语:World Wide Web),亦作“WWW”、“Web”,是一个由许多互相链接的超文本组成的系统,通过互联网访问。 万维网并不等同互联网,万维网只是互联网所能提供的服务其中之一,是靠着互联网运行的一项服务。 互联网和万维网用语经常被使用且没有太多区别。然而,两者是不一样的。互联网是电脑网络互相连接的全球系统。相较之下,万维网是全球收集的文件和其他资源,通过超链接和 URIs 连接。万维网资源通常使用 HTTP 访问,这是互联网通信协议的一种。 万维网的核心部分是由三个标准构成的:
- 统一资源标识符(URI),这是一个统一的为资源定位的系统。
- 超文本传送协议(HTTP),它负责规定客户端和服务器怎样互相交流。
- 超文本标记语言(HTML),作用是定义超文本文档的结构和格式。
总结来说,web 是一个由许多相互链接的超文本组成的系统,它使用 URI 来定位系统中的每一个资源,并通过 HTTP 协议进行数据的交互。 更抽象的说,Web 是一个分布式信息系统,为超文本文件和其他对象(资源)提供访问接口和访问机制。 理解了什么是 web,我们便可以更好地理解什么是 REST 了。作为 web 自身的架构风格,我们直接给出结论:REST 本质上是一种分布式超媒体系统的应用层解决方案,它为资源互通和资源管理的分离提出了一系列架构约束和原则,得到一个功能强、性能好、适宜通信的以网络为基础的应用软件架构。 这个结论依然很难理解,但我们需要对此有一个概念了解。接下来我们会对 REST 进行更为详细的介绍。
要理解 REST,首先需要理解(Resource)Representational State Transfer 这个词组。
REST 对于信息的核心抽象是资源。任何能被命名的信息都能作为一个资源:一份文档、一个与时间相关的服务(例如,“洛杉矶今日的天气”),一个其他资源的集合、一个非虚拟的对象(例如,人)等等。 换句话说,可以作为创作者的超文本引用的目标(the target of an author's hypertext reference)的任何概念都必须符合资源的定义。资源是到一组实体的概念性映射(a conceptual mapping),而不是在任何特定时刻与该映射相关联的实体本身。 更精确地说,资源 R 是一个随时间变化的成员函数该函数根据时间 t 将资源映射到一个实体或值的集合,集合中的值可能是资源表述(resource representations)和/或资源标识符(resource identifiers)(两者是等价的)。 对于一个资源来说,唯一必须静态的是映射的语义,因为语义才是区别资源的关键。 正是资源的这个抽象定义,使得 Web 架构的核心功能得以实现。首先,它包含了很多信息的来源,并没有人为地通过类型或实现对它们加以区分,从而实现了通用性。其次,它允许引用到表述的延迟绑定,从而支持基于请求的性质来进行内容协商。最后,它允许创作者引用一个概念而不是引用此概念的某个单独的表述,从而使得当表述改变时无需修改所有的现有链接(假设创作者使用了正确地标识符)
如何来理解“对于一个资源来说,唯一必须静态的是映射的语义,因为语义才是区别资源的关键”这句话呢?举一个简单的例子来说明一下: “一个 APP 的当前版本”是一个资源,而“一个 APP 的最稳定版本”也是一个资源,尽管这两个资源在某个时刻上可能会映射到相同的值,但它们是是截然不同的,且两个资源能够被单独地标识和引用。
REST 使用资源标识符来表示组件之间交互所涉及的特定资源。REST 连接器提供了访问和操作资源的值集合的一个通用的接口,而无须关心其成员函数(membership function)是如何定义的,或者处理请求的软件是何种类型。由命名权威(naming authority)来为资源分配资源标识符,使得引用资源成为可能,映射的语义有效性也由同样的命名权威来负责维护(例如,确保成员函数不会改变)。 传统的超文本系统通常只在一个封闭的或局部的环境中运行,它们使用随信息的变化而改变的唯一节点或文档标识符,并依赖链接服务器(link server)以独立于内容的方式来维护引用。因为集中式的链接服务器完全无法满足 Web 的超大规模和跨越多个组织领域的需求,所以 REST 采用了其他的方式——以来资源的创作者来选择最符合被标识的概念本质的资源标识符。
资源标识符为访问和操作资源的值集合提供了一个通用的接口。换句话说,我们抽象出来的资源都应该是可标识的,都应该拥有一个明显的 ID——在 Web 中,代表 ID 的统一概念是:URI(统一资源标识符)。URI 构成了一个全局命名空间,使用 URI 标识关键资源意味着这些资源获得了一个唯一、全局的 ID。 举个简单的例子:如果在一个类似于 Amazon.com 的在线商城中,没有用唯一的 ID(一个 URI)标识它的每一件商品,可想而知这将是多么可怕的业务决策。
资源的表述是一段对于资源在某个特定时刻的状态的描述。 资源在外界的具体呈现,可以有多种表述 (或称为表现、表示) 形式,在客户端和服务端之间传送的也是资源的表述,而不是资源本身。例如文本资源可以采用 html、xml、json 等格式,图片可以使用 PNG 或 JPG 展现出来。资源的表述包括数据和描述数据的元数据,例如,HTTP 头“Content-Type”就是这样一个元数据属性。 更确切的说:
REST 组件使用表述来捕获某个资源的当前状态或预期状态,随后在组件之间移交该表述,同过这种方式在资源上执行各种动作(perform actions on a resource)。表述(representation)有一个字节序列和描述这些字节的表述元数据(representation metadata)构成。表述的其他常用但不精确的名称包括:文档、文件、HTTP 消息实体、实例或变量。 表述由数据、描述数据的元数据、以及(有时候存在的)描述元数据的元数据组成(通常用来验证消息的完整性)。 表述的数据格式被称为媒体类型(media type)。
简单总结一下:
状态转移:在客户端和服务器端之间转移(transfer)代表资源状态的表述。通过转移和操作资源的表述,来间接实现操作资源的目的。 访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。 互联网通信协议 HTTP 协议,是一个无状态协议。这意味着,所有的资源状态都保存在服务器端。因此,如果客户端想要操作服务器中的资源,必须通过某种手段,让服务器端的资源发生"状态转移"(State Transfer)。而这种转化是建立在表述之上的,所以就是"表述性状态转移"。 客户端使用的手段,在 web 中就是 HTTP 协议。具体来说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:
Resource Representational State Transfer,资源表述性状态转移,即就是:根据数据抽象出来的资源,以某种表现形式,通过某种手段,在网络中发生状态转移,以此来间接实现操作资源的目的。表述性状态转移(REST)架构风格是对分布式超媒体系统中的架构元素的一种抽象。 在 web 中,具体而言:
我们再来换一个角度,以搭建系统的角色来思考这个问题: 在 web 中,为了获取我们需要的分布在不同地域的超媒体资源,我们该如何设计这个系统?显然,web 中有着大量的,分布在不同地方的各种类型的资源。我们需要提供的是一个大型分布式超媒体系统的应用层解决方案。 首先我们需要为所需的数据设定唯一标识,因此我们将数据进行抽象为资源,并使用统一资源标识符(URI)为每个资源设定 ID,这样我们就有办法来操作每个资源。 那么该如何操作资源呢?换句话说,当我们看到一个 URI 并将它输入到浏览器中是,为何浏览器知道该怎样处理这个 URI?事实上,浏览器知道如何去处理 URI 的原因在于:所有的资源都支持同样的接口(URI),支持一套同样的方法(HTTP 动词)。这样,当我们自己按照这种方式来定义我们自己的资源时,web 中的其他人便可以轻松的获取这些资源。 获取资源时,我们可能需要不同的呈现方式或是需求,因此我们需要对资源进行表述,使其表现为我们需要的形式。
从分布式系统的角度来看 REST,我们发现以资源为核心的 REST 确实提供了一种解决大型分布式资源系统的解决方案,而 web 的成功也确实验证了这套理论的正确性。
REST 作为一种组织 web 服务的架构风格,提出了一系列架构级约束。如果一个系统满足这些约束,那该系统就被称为是 RESTful 的。接下来,我们会逐条说明 REST 的五条必要约束。
通信只能由客户端单方面发起,表现为请求 - 响应的形式。
客户 - 服务器约束背后的原则是分离关注点。通过分离用户界面和数据存储这两个关注点,我们改善了用户界面跨多个平台的可移植性;同时通过简化服务器组件,改善了系统的可伸缩性。然而,对于 Web 来说,最重要的是这种关注点的分离使得组件可独立地进化,从而支持多个组织领域的互联网规则的需求。
我们接下来在为客户 - 服务器交互添加一个架构约束:通信必须在本质上是无状态的,从客户到服务器的每个请求都必须包含理解该请求所必需的所有信息,不能利用任何存储在服务器端的上下文,会话状态因此要全部保存在客户端。
前面我们分析 REST 词组时,提到了资源的状态转移,而在这里,REST 约束中又包含了无状态通信原则,看起来仿佛是矛盾了:既然“无状态”,又怎么能说“状态转移”呢? 其实,这里说的无状态通信原则,并不是说客户端应用不能有状态,而是指服务端不应该保存客户端状态。
状态应该区分应用状态和资源状态,客户端负责维护应用状态,而服务端维护资源状态。客户端与服务端的交互必须是无状态的,并在每一次请求中包含处理该请求所需的一切信息。服务端不需要在请求间保留应用状态,只有在接受到实际请求的时候,服务端才会关注应用状态。 这种无状态通信原则,使得服务端和中介能够理解独立的请求和响应。在多次请求中,同一客户端也不再需要依赖于同一服务器,方便实现高可扩展和高可用性的服务端。
由于不能将状态数据保存在服务器的共享上下文中,因此增加了一系列请求中发送的重复数据(每次交互的开销),可能会降低网络性能。此外,将应用状态放在客户端还降低了服务器对于一致的应用行为的控制能力,因为这样一来,应用就得依赖多个客户端版本的语义的正确实现。
为了改善网络的效率,我们添加了缓存这个架构约束。缓存架构要求一个请求的响应中的数据被隐式地或显式地标记为可缓存的或不可缓存的。如果响应是缓存的,那么客户端缓存就可以为以后的相同请求重用这个响应的数据。
添加缓存可能部分或全部消除一些交互,从而通过减少一系列交互的平均延迟时间,来提高效率、可伸缩性和用户感知的性能。
如果缓存中陈旧的数据与将请求直接发送到服务器得到的数据差别极大,那么缓存会降低可靠性。
使 REST 架构风格区别于其他基于网络的架构风格的核心特征是,它强调组件之间要有一个统一的接口。通过在组件接口上应用通用性的软件工程原则,简化了正特的系统架构,也改善了交互的可见性。实现与它们所提供的服务是解耦的的,这促进了独立地可进化性。 然而,需要的付出的代价是,统一接口降低了效率,因为信息都使用标准化的形式来移交,而不能使用特定于应用的需求的形式。REST 接口被设计为可以高效地移交大粒度的超媒体数据,并针对 Web 的常见情况做了优化,但是这也导致该接口对于其他形式的架构交互而言不是最优的。
为了获得统一的接口,需要有多个架构约束来指导组件的行为。REST 由四个接口架构约束来定义:
每个资源都拥有一个资源标识。每个资源的资源标识可以用来唯一地标明该资源。
这里说的是资源的自描述性。一个 REST 系统所返回的资源需要能够描述自身,并提供足够的用于操作该资源的信息,比如如何对资源进行添加,删除以及修改等操作。也就是说,一个典型的 REST 服务不需要额外的文档对如何操作资源进行说明。
消息的自描述性。在 REST 系统中所传递的消息需要能够提供自身如何被处理的足够信息。例如该消息所使用的 MIME 类型,是否可以被缓存等。
即客户只可以通过服务端所返回各结果中所包含的信息来得到下一步操作所需要的信息,如到底是向哪个 URL 发送请求等。也就是说,一个典型的 REST 服务不需要额外的文档标示通过哪些 URL 访问特定类型的资源,而是通过服务端返回的响应来标示到底能在该资源上执行什么样的操作。一个 REST 服务的客户端也不需要知道任何有关哪里有什么样的资源这种信息。
这个描述的核心是超媒体概念,换句话说:是链接的思想。链接是我们在 HTML 中常见的概念,但是它的用处绝不局限于此(用于人们网络浏览)。考虑一下下面这个虚构的 XML 片段:
<order self="http://example.com/customers/1234"> <amount>23</amount> <product ref="http://example.com/products/4554"> <customer ref="http://example.com/customers/1234"> </customer> </product></order>
如果你观察文档中 product 和 customer 的链接,就可以很容易地想象到,应用程序(已经检索过文档)如何“跟随”链接检索更多的信息。当然,如果使用一个遵守专用命名规范的简单“id”属性作为链接,也是可行的——但是仅限于应用环境之内。使用 URI 表示链接的优雅之处在于,链接可以指向由不同应用、不同服务器甚至位于另一个大陆上的不同公司提供的资源——因为 URI 命名规范是全球标准,构成 Web 的所有资源都可以互联互通。 超媒体原则还有一个更重要的方面——应用“状态”。简而言之,实际上服务器端(如果你愿意,也可以叫服务提供者)为客户端(服务消费者)提供一组链接,使客户端能通过链接将应用从一个状态改变为另一个状态。目前,只需要记住:链接是构成动态应用的非常有效的方式。 对此原则总结如下:任何可能的情况下,使用链接指引可以被标识的事物(资源)。也正是超链接造就了现在的 Web。
为了进一步改善与互联网规模这个需求相关的行为,我们添加了分层系统架构约束。分层系统风格通过限制组件的行为(即,每个组件只能“看到”与其相交互的相邻层),将架构分解为若干层级。通过将组件对系统的知识限制在单一层级内,为整个系统的复杂性设置了边界,并且提高了底层独立性。我们能够使用层级来封装遗留服务,使新的服务免受遗留客户端的影响,做法是将不常用功能转移到一个共享中间组件中,从而简化组件的实现。中间组件还能够通过支持跨多个网络和处理器的负载均衡,来改善系统的可伸缩性。
中间件: 中间件是一种独立的系统软件或服务程序,能够连接两个独立软件或系统。分布式应用软件借助于中间件能够在不同的技术之间共享资源。即:中间件使得若干个相互独立的系统,在各自都拥有着不同的接口的情况下,仍然能通过中间件来实现通信。执行中间件的一个关键途径是信息的传递。通过中间件,应用程序可以工作在多个平台及 OS 环境中。简而言之,中间件即桥梁。
分层系统的主要缺点:增加了数据处理的开销和延迟,因此降低了用户感知的性能。对于一个支持缓存架构约束的基于网络的系统来说,可以通过在中间层使用共享缓存所获得的好处来弥补这一缺点。
REST 架构风格由一组经过选择的架构约束组成,通过这些架构约束在候选架构上产生所期待的架构属性。尽管能够独立考虑其中每一个架构约束,但是根据它们在公共架构风格(common architectural styles)中的来源来对它们进行描述,使得我们理解选择它们背后的基础理论更加容易。
本文试图从本质上来理解什么是 REST。 我们首先从 REST 的起源说起,发现 REST 与 Web 之间的本质关系,并从 Web 的特性,得到 REST 本质上是一个分布式超媒体系统的应用层解决方案这一结论。接着我们对 REST,即(Resource)Representational State Transfer(资源表述性状态转移)这个词组进行了详细分析,进一步得到了 REST 以资源为核心的架构风格。最后,我们对 REST 架构的五条必要约束条件进行进一步的阐述和说明,以便读者能够更为深刻地理解 REST。 这篇文章到这里就算是结束了,笔者在写下这些内容的时候依然时时感到自己知识的匮乏,以致无法更为深刻地理解 REST。笔者的这篇博客,既是希望能对自己所学做一个总结,也希望能给其他初学者带来一点帮助。文中若有理解不当的地方,欢迎批评指点。