任何概念从产生到付诸实施似乎都要经历一个同样冗长的过程,从被提出,误解,诠释,再认识,到应用,好像没有几个技术观点能够跳过这些,现在的RESTful架构似乎也正处在这个过程的中段。互联网上关于REST的介绍性文章可谓铺天盖地,园子里的帖子也有不少,但是其中大多数都是一个对REST这一概念的精辟概述,比较流行的样式是一句话总结或将其特性列表,这样的总结对于读者来说的确提供了较大的便利,它们容易被人记住,也的确反映了从最终使用者角度来看REST最为显著的几个特性,但是它们同时也很容易让读者觉得了解了这些就几乎了解了REST的全部,而不会继续探究细节,也许这就导致了许多朋友对REST理解较为片面的原因吧。比如我看到有些朋友认为操作HTTP原语就是REST,有些人认为只要在URL上花心思了就是REST,其实他们的看法都表现了基于HTTP和URL的REST架构风格在实现层面的重要组成部分,但以其来定义REST本身未免过于草率。于是,我便有了写这系列随笔的想法。我并不是为了向大家介绍REST,而是想和大家分享下自己对这个架构风格的理解,如果能够以此解答大家对于REST的一些困惑的话,那是我的荣幸。
相信任何关于REST的介绍性文章都会提到Roy T. Fielding博士在2000年发表的一篇论文,然而对这篇文章本身的关注度似乎比对它的引用要少的多。这篇论文本身并不仅仅是为提出REST而作的,而是介绍了一种以架构风格为指导的,针对网络应用架构设计的方法论,而REST是被作为这种方法论的应用推导出的一种适应分布式超媒体系统的架构风格。文中作者将架构风格定义为:
“一组协作的架构约束,这些约束限制了架构元素的角色和功能,以及在任何一个遵循该风格的架构中允许存在的元素之间的关系。”
这里的“架构约束”指的是对系统在运行时表现出的某组属性的需求。举例来说,如果期望“系统具有可伸缩性”(系统属性),那么在Web系统中对应的架构约束之一就可以是“在服务器上不保留会话状态”。一般来说,基于Web的系统所关注的属性主要有性能(Performance),可伸缩性(Scalability),可扩展性(Extensibility),可移植性(Portability)和可靠性(Reliability)等,而把与其相对应的相关约束进行不同的组合便形成了各种架构风格。比如最为常见的Client-Server风格,就是与可伸缩性和简单性等对应的约束集合。Fielding博士在论文中对基于架构风格的分类有详细论述,这里就不多做解释了。基于前面对架构风格的定义,Fielding博士从识别互联网应用需求开始一步一步推导出了早期互联网的架构约束,并在此基础上添加现代分布式超媒体系统所需要的属性,从而获得一个全新的架构风格,它表现了现代互联网背后的运转模型,并满足分布式超媒体系统提出的约束条件,这便是表述型状态转移(REpresentational State Transfer, REST)架构风格产生的过程。
这个过程意味着两个事实,首先,由于REST是为了获得某种系统属性的架构约束集合,所以它不与任何具体实现相关联。任何能够满足这组特定约束的实现都是可以被称为REST式的架构。虽然这一结论已经有口皆碑,许多帖子或介绍性文章也都会有类似于“...REST首先只是一种架构样式,不是一种标准...”这样的话,然而在我自己的学习过程中,这些简要的概述却着实在理解上给我带来了一些麻烦。曾经困扰过我的问题有“架构样式与标准有何区别”,“为什么REST算是一种架构样式”,“CS也算是一种架构样式那么怎样区分将其与REST区分开来”等等,而在了解了REST的推导过程后这些问题便迎刃而解了。
另外,纵观REST的推导过程,可以发现这一架构风格与互联网紧密结合,我不是说在使用的技术上,而是在期望实现的架构属性方面,REST是基于互联网所表现的属性之上的。属性代表着系统运行时表现出的功能,也就是说REST风格架构的适用场合必然与互联网密切相关。互联网创始人Tim Berners Lee说过这样的话:“The web is the universe of globally accessible information”,它点出了互联网用于分享资源的属性。那么我们能不能以此推论与之具有相似架构约束的REST风格也是面向资源的呢?答案是肯定的,而上面简短的推导过程也正是“REST是面向资源的(Resource-Oriented)”这一论述的理论依据。
资源作为REST架构风格的一个核心元素就其本身来说是没有多大价值的,它们的价值来源于被人发现和使用,以及与其他资源间的交互。为了实现这一属性,REST引入了称为资源标识符(Resource Identifier)的架构元素,它们表现为一种查找和获取资源的方法,使得资源具有可寻址性(addressability),在互联网(作为REST架构的一个实现)中资源标识符就是URL了。接下来被发现的资源必须通过一种方式被使用者认识和使用,它们需要向客户端“介绍”自己,有时候为了迎合客户端的口味还需要以多种方式去“展现”自己,或展现“过去的自己”,这种展现在REST中便是资源的表述(Representation)。每一个资源都是其表述的来源(source),一个资源可以有多种类型的表述。举例来说,“SNOOPY”这一资源可以被表述为一份描述一只狗的XML或是一幅漫画,而无论它被描绘成哪种形式,其对于使用该资源的客户来说都是长篇漫画Peanuts中的主角。从概念上来看,这和语义网(Semantic Web)的构想是相似的,他们都有通过“一组正式的、被明确定义的词汇,来记录和描绘一个普遍认知”的需求。前面提到资源可能会需要展现“过去的自己”,这可以被理解资源对其自身在某一个特定状态(State)下的表述。举例来说,上周我从图书馆借回来了一本名为<everyday italian>的书,由于发现书中部分内容与我了解的不一致,我添加了一些批注。于是乎接下来所有这本书最新版本的请求都变成了由我添加过批注的这个版本(假设该书可供借阅的数量只有一本,而备份版本存于书库,需要提交申请),也就是说这个资源的状态(Resource state)被“添加批注”这个动作改变,而对该书最新版本的请求意味着对资源当前状态的表述的请求。我的批注惹恼了作者的一些FANS,于是有许多对备份版本的请求提交到了图书馆,类似于这样“请给我该书2009年6月12日以前的最新版本”。图书馆管理员在收到这样的请求后首先对我这样的读者发了句牢骚,然后根据索引号去书库查找备份版本。在这个场景中,最终用户得到的表述资源状态发生了改变,从最新版本变为了某一特定时间的历史版本,而资源本身并未改变(这里我将该书作为一个资源看待)。这里客户端获得的始终是资源的表述状态,对资源状态的表述在请求响应过程中从服务端被转移(Transfer)至了客户端,而在被转移的状态中又包含着将请求方引导至其他状态的连接(Hyperlinks)。资源,表述,状态,转移这几个元素便成为了表述型状态转移(REpresentational State Transfer)这一架构风格的基本要素。
以上从架构元素角度定义了REST风格,而在资源的连接与交互方面,REST也提出了为实现系统可伸缩性、组件可替换性以及性能优化等属性的要求。这些约束中统一接口,无应用状态等是最为关键的几点,对他们的论述网络上也比比皆是,比如发布于InfoQ上的<A Brief Introduction to REST>就很详细地解释了使用标准化方法与资源进行交互和无状态沟通的含义和原因,这里就不再赘述了。另外,我在过去的一篇帖子里也简单谈了一些自己对无状态这一概念的理解,有兴趣的朋友可以参考一下,而对于基于HTTP和URL的REST中,统一接口和无应用状态的实际表现形式,例如对HTTP原语的使用等,我会在接下来的几篇随笔里与大家分享我的认识。
REST源于互联网也用于互联网,这也就意味着REST对其适用场合有着默认的假设。由于REST将互联网作为一个开放的发布媒体,而不是一个通信媒介,所以REST期望其应用场景是一个为了通过网络公开并分享某些资源的分布式超媒体系统,这些资源可以是静态文本或图片,也可以是系统处理数据的能力。这也就意味着REST其实并不适用于一些业务逻辑复杂的企业应用,此类应用通常不希望任何与业务过程不相关的实体分享信息,而它们在其他一些方面的特殊要求,如服务器端对客户端应用状态的保留和复杂的安全基础设施等,都是与REST架构风格中某些约束相冲突或REST没有涉及的。所以硬将REST风格套用到这样的企业应用上,我觉得着实有点勉为其难。REST中的组件复合模式也同样说明了这点。考虑下现在的一些信息门户网站,许多信息的展现都是通过从多个来源获取与相关的数据或功能来完善的,这也就是我们常说的mashup,而mashup便是REST所期望的信息(资源)复合模式。在这一形式中,资源与资源通过超链接连接成一个复合的表述,而不受某个高层工作流或既定业务流程的控制。与之相对的,企业应用中的数据或功能通常是被作为某个业务流程中的一个组成部分被复合到一起的,这一流程指导着数据或功能的使用时机以及下一步的交互对象。在这个模式下服务器端维护状态信息几乎成为了必然,当然可以将状态信息存于客户端,并通过将其包含于每个请求发送至服务端的方式来实现REST式的架构,但是这样做冲销了REST的简洁性的优势,也可能增加网络传输的负担。
以上便是我对于REST架构风格的一些个人体会,从解释REST的推导过程到其适用场合的假设,希望这些理论味道有点浓的文字能够给大家带来一些小小收获。接下来的几篇随笔里我会主要和大家聊一下自己基于HTTP和URL的REST架构中一些元素的理解,讨论一些我在学习过程中看到的争论,发表一下自己的看法。