面向资源的架构:REST的另一面

作一个假设,假如把你送回万维网出现以前。想象一下你如何去跟当时那些毫无准备的人们解释那即将发生的重大变化。结果很可能是他们根本无法深入地理解万维网的出现将会对他们生活的每个方面产生的影响。在回顾中,那种感觉就像一场突如其来的海啸,永远地改变了我们身边的景象。而现实可就朴实得多了,我们现在所经历的这些结果都是由一系列深思熟虑的技术选择及它们之间的相互作用所造成的。

这一浪潮同样历经了多年的努力,并且同样是由认真思索后所选择的特定技术来驱动的。通过这些选择的组合,可以解决因不断发生的技术变化、业务膨胀、新的及变更的数据源、以及那些普遍存在的既昂贵而又易出故障的花了大价钱却又不能交付相应价值的项目所带来的问题。Web服务与面向服务的架构原本被看做是解决问题的一个答案,然后他们那优雅的愿景却永久地因为拙劣的技术解决方案而染上了污点。而现在,稍作停顿,想一想把你自己放在那些曾经对Web保有怀疑态度的人们所处的位置,正要被别人开化。除非你保持足够的警觉,不然你又会再一次毫无准备地面对新一轮经济上、社会化、技术化、以及组织形式上的冲击!尽管这种改变令人感觉发生在一夜之间,实际的情况是它们都经历了经年的积蓄,而现在才产生了有形的结果。这种新的浪潮就是Web的发展已经逐渐超越了文档,而成为了数据的网络,包括个人的和私有的。我们不再关注信息的载体,而转向关注信息本身以及它们如何联系。

可喜的是我们并非是从零开始。我们是建立在原有的数据Web的技术的发展之上的。我们可以将数据库、软件包、服务以及其它的内容源打包成新的抽象集合,从而帮助我们摆脱过去那种单调乏味的工作。我们将公共的数据Web与我们自己的私有数据集成起来。不断渐进地采用这些技术将会收获新的能力,而这些能力又能释放出更多的能力。

本文是这系列文章中的第一篇,该系列突出了面向信息系统的演化、告诉我们位于什么位置,并给出了我们发展的路线图。不管从表面上看是怎样,这些选择不是随机或神秘地做出的决定,而是基于学术与应用工程的长期传统而作出的根本抉择。

我们重新回到表象化状态转移(REST)架构风格。它经常被引用,可更多的是被误解,这一构建网络软件系统的方式允许我们将文档,数据和面向信息的服务融合成一个丰富的具名资源的逻辑性生态系统。从这里开始,我们将引入语义网的愿景,并领略由灵活与扩展兼备的数据模型和查询能力所代表的语义网的核心技术。我们将看到如何将关系型数据、来自文档的内容、电子表格、RSS feed等等包含进一个由可重用内容组织的富Web。

在展示了这些基础之后,我们将会去领略各种构建于这些技术之上的成功项目,并回来重新恢复Web服务技术所支持的曾对我们许诺过愿景。我们将会描述一个流程,我们可以以此达成一个信息系统统一理论;它不仅能处理,并且将拥抱那些过去痛苦而又棘手的技术性和社会性变更。

关于语义网的炒作已经太多了,但同时也有一股稳定的分支在默默的通向成功。这一系列文章对新的以及已熟知的领域都会作一个指引。但我们将会从你未曾看到过的更深的层次将这些技术联接起来。我们将会强调企业,政府组织以及标准实体所发生的事件和行动,以表明我们是正处于这样的进程中,并且这将改变一切。我们将展现通过微妙的视角转换和施行促进变更的标准将会对你的系统实现带来多么大的变化。

第一步,是为基础设施的所有方面采用公共的命名模式。一个只关注服务的架构往往忽略了流经系统的数据流。到头来,我们的组织第一个关注的是信息。REST和Web架构将这一优先级摆在了前面,并为我们后面的讨论做好了铺垫。

Rest的其他方面

在与复杂性的斗争中,人们讨论表象化状态转移(REST)已成为了一种时尚。这场战斗的敌人,据某些人的观点,是SOAP以及围绕它的Web服务技术栈。这种敌我的修辞给论点带来了激情,但却很难产出有意义的对话,所以人们对于其蕴含的消息以及其重要性仍然感到困惑。REST的目标不是代替SOAP;而是构建更好的系统。

REST甚至不是SOAP的直接替代。它并非什么方便的技术,或者所谓的通过URL调用Web服务的简单解决方案。对于信息资源的管理与调用随意性的行为本就不是同一回事,这种混淆使得人们构建出的所谓"RESTful"解决方案,既非RESTful,也不是好的解决方案。

REST的益处既得自于它的限制又得自于它的灵活性。如果你读过Roy Fielding博士的论文(你值得一读),你会明白其意图是描述特定的架构性约束是如何组合起来而产生了一系列我们在网络软件系统里所期待的属性。采用统一的接口,赫赫有名的统一资源定位器(URL)对REST的定义有贡献,但仍不足以定义它。同样的,仅仅是通过URL来暴露随意性的服务接口,也无法产出我们所看到的暴露Web同样成功的益处。它要花费更丰富的一系列交互与系统划分才能得到完整的结果。

大多数人的理解是,REST牵涉到使用几个动词通过URL来请求和提供信息资源的应用信息。你对URL发起GET请求来抽取信息,你通过POST和PUT来创建和更新,而DELETE请求用于移除信息。

这一总结并没有错,但它只是其中很小的一部分。不幸地是,它省略掉的部分所产生的自由度往往使得人们作出错误的决定。在这种鸿沟中,人们脱离动词来创建URL,这就消除了给事物命名所带来的好处。 他们认为REST仅仅关心CRUD操作。他们会创建一些神奇的不相关的URL,你必须事先知道如何解析,因此丧失了超文本引擎的可发现能力。而最不可原谅的,就是创建的这些URL单独与特定的数据格式关联起来,过早地对客户端的信息状况作了决定。

理解REST整体的含义有助于你避免这些问题;它能帮助你开发强劲的、灵活的、可伸缩的系统。但这同时也是对于信息以及它是如何被使用的一种全新理解的一个起点。在这样的Web架构基础之上,应用语义网的其它技术就会产生出前所未有的力量,而对我们作为个人,作为政府,组织等等如何相互进行交互产生影响。这也解释了我们为什么一来就深入到REST的那些不怎么为人所知和被人谈及的部分。这些部分的含义包括以下主题:

  • URL作为标识符
  • 自由的形式
  • 逻辑连接的延迟绑定系统
  • 超文本作为状态转移的引擎(HATEOS)

URL作为标识符

我们已经建立了这样一种前提,大多数人都知道URL和REST。比较明显的一点是人们都知道把URL用作调用服务,但人们对于把URL作为信息的命名这种宏观的概念还不是很熟知。名字是我们用来标识人员、地点、物体和概念的。如果我们没有这种标识的能力,我们就没有表示的能力。每天想想Abbott和Costello的臭名昭著的诙谐剧“誰在一壘”。有了命名我们就可以消除歧义,并在一定的上下文范围内明确地标识我们所关心的东西。有了命名和公共的上下文我们就能在这一范围内明确的对命名的事物进行参照。

统一资源标识符(URI)是父级模式。它是用于编码其它模式的一种方法,取决于你是否想要包含进它们的解析信息。图书管理员和其它的长久数据管理员都喜爱不会改变的名字。统一资源名称(URN)是不包含定位信息的URI;它只包含名字。好处是这些名字不会失效。而坏处是这些名字没有解析过程。一个URN的例子是书籍的ISBN号:

urn:isbn:0307346617

为了查找关于这本书的更多信息,你需要找到一个可以根据ISBN号来查找信息的服务。

如果我们的系统和信息的上下文不会有改变的话,我们也许会始终把解析信息包含在资源名称里以便解析。但只要是遇到过失效的连接的人都能理解对于一些重要的事物我们会想要长期有效的名字。回顾我们使用URL的历史,我们做了不少错事,比如:

http://someserver.com/cgi-bin/foo/bar.pl
http://someserver.com/ActionServlet?blah=blah
http://someserver.com/foo/bar.php

这些URL的问题在于,用于产生结果的技术与信息的消费者毫无关系。这样创建URL没有任何站得住脚的理由。应该关注的是信息,而不是技术。实现技术总是在不断更新。如果你摒弃了他们,举个例子,任何一个链接到基于Perl,Servlet或PHP的URL就会失效。接下来的文章中我们会讨论到处理这一问题的一些基础设施,而现在,我们将只关注对于命名我们的信息资源如何作出明智的选择。

尽管脆弱,但URL模式确实为我们提供了在全局的上下文里消除信息引用歧义的能力。

http://company1.com/customer/123456

就能很好的区别于

http://company2.com/customer/123456

而像“123456”这样的非上下文的标识符则不能。

将这一理念根植于更广泛的信息系统框架,你可以将URL想象成不特定于任何具体数据库的主键。我们可以在各种不同的数据库,文档,应用中通过URL来引用一个条目,并且可以确定我们引用的是同一个事物,因为在整个全局的上下文中它有一个唯一确定的名字。在后续的讨论中我们将使用这一特性来描述RESTful系统并将其与其它内容和元数据连接起来。

URL另一个值得讨论的方面就是它们的统一适用性。我们有着一个公共的命名模式可以去识别:

  • 文档(报告,博客,公告)
  • 数据(成果,实例信息,元数据)
  • 服务(REST!)
  • 概念(人员,组织,领域特定术语)

我们不需要创造一种另外的机制来参照每个不同类别的物件。某些特定准则的严谨的应用允许我们模糊这些物件的区别,这就把我们领到了关于URL的最后一点。这些名字不仅对于参照我们所关心的信息是有用的,而接收这些引用的系统也可以轻易的请求它们。URL中的“L”(定位器)给予了我们就算不了解它的其它任何情况,也能解析它的能力。对于我们命名的物件,通常我们都可以对其调用同样的基础操作。对于以URL的形式所表示的文档,数据,或者是产生该数据的服务,甚至是一个抽象的,不可通过网络访问的概念,发起一个GET请求工作原理从根本上来说是相同的。对于我们被授权操作的这些事物,我们也可以以同样的方式来创建,修改或者是删除它们。

自由的形式

我们对于Web的经验就信息的形态来说仍显被动。当我们点击链接时,我们期望内容以某种特定的形式返回,通常是HTML。对于许多类型的信息来说这是很好的,但架构对于更多的会话风格提供了丰富的支持,使得客户端可以用它们更青睐的形式请求信息。

要理解它的作用所在,可以考虑一个公司的销售报表。可以想象得到对于管理层、销售人员、其它的员工、客户和投资者而言这将是衡量公司业务表现的一个有效的指标。这种报告的名字可以在URL中包含进年份和季度:

http://company1.com/report/sales/2009/qtr/3

我们可以将它与3月份的销售报表作个对比:

http://company1.com/report/sales/2009/month/3

两者都是很好的有逻辑内涵的名字,而且不会因时间而失效。人们可以轻易的在浏览器输入这样一个URL然后就能以HTML的形式得到他们寻找的信息,这种愿景非常诱人。这个报表可以被添加到书签,通过电邮发送,被链接到,我们所钟爱的Web的方方面面。

题在于,信息锁定在表现形式中(除非我们在本系列稍后引入GRDDL和RDFa这样的技术后!)。我们习惯于从页面获取内容,但却放弃了而只获取摘要。随着页面的排列改变了,我们的脚本也就失效了。

如果你曾是公司的一名编程人员,而你想要直接获取信息的话,你可能会想要通过XML去请求它。你可以得到原始的结构化数据,你可通过模式来验证。HTTP和REST让这变得易如反掌,只要服务器知道该如何响应。通过在请求中传递一个“Accept:application/xml”的头部信息,你可以表明你请求的是(或你的需求是)XML。成功的话,你将会得到一个字节流的MIME类型,以表明你的请求被接受了。如果失败,服务器将通过一个406错误表示无法帮助你。在这种情况下,你也许会想要联系负责该信息的部门并要求他们加入你所需要的支持;他们既能做到而又不会影响现有的客户。 如果你是业务分析师,你会觉得XML过于粗糙而有点棘手,你可能想要返回电子表格,这一格式将非常适宜于融入你现有的工作流,工具和流程。

关键点在于该报表的逻辑名字在它被请求的时候可以轻易的转换成多种不同的格式。而运行能够接受这些各种各样修改后的格式的系统也是同样的容易。客户不能看到信息是如何被存储的真实情况,他们只需要知道它能工作就行。而这样的自由度竟未被构建RESTful的人员加以完全的利用。当他们搭建一个服务并决定仅仅返回XML的时候,他们忽略了REST原本可以为组织提供的潜在价值。

许多开发者出于不了解内容协商,或者觉得很难在浏览器里对它进行测试,他们就对不同的格式定义了不同的URL:

http://company1.com/report/sales/2009/qtr/3/report.html
http://company1.com/report/sales/2009/qtr/3/report.xml
http://company1.com/report/sales/2009/qtr/3/report.xls

一旦你越过的特定用途的约束,这样的开发者捷径反而会变成一种局限。本质上讲,我们现在有三种信息资源,没有一个可以以其它的形式来展示。这不仅在全局上下文中创建了身份标识的分支,它同时还过早的向客户端承诺了特定的形式。如果你将一个对URL的引用作为工作流或者是编配的一部分,那你就掠夺了上游客户自由选择数据格式的权益。

有几种不使用浏览器测试正确的RESTful服务的方法,例如:

curl -H "Accept: application/xml" -O http://company1.com/report/sales/2009/qtr/3

使用流行的curl程序,任意一个合理的HTTP客户端都会提供类似的功能。

支持可协商数据格式的富生态系统所带到的益处也许并不是立竿见影的,但一旦你投入进去,你就会发现它是迈向一个生命长久而灵活的系统的关键,它有益于客户,而不是开发者。

逻辑上连接的延迟绑定系统

一旦你为信息资源作出了良好的逻辑性的命名,你就会发现因这样的决定而带来的附加的好处。命名的引用可以安全而高效地作为结果传递回去,而不用返回实际的数据。这对于大量而敏感的数据有着极大的意义,但同时这也意味着可能出现技术和架构的迁移。

出于指针在C和C++语言的作用同样的原因,URL作为数据的引用而传递给信息的消息者是一种更为紧凑和高效的方式。诸如金融交易,卫星照片这样的大数据集可以在工作流中引用而不必要求所有的参与者都去承担处理大容量内容的压力。

何接触到实际数据的编配都必须考虑到将其传递给其它系统可能带来的安全性影响。很快就会发现要提供哪些人在流程的每个步骤被允许做什么的精确信息是难以维持的。如果一个引用一步一步地被传递,那就该是信息源本身来保障访问权限。有些步骤不要求访问敏感的信息,因此在它们解析引用的时候就可以排除它们接收信息。

这意味着延迟绑定解析可以在整个请求的上下文中发挥作用。使用某一应用访问一个资源的特定用户也许有出于业务需要查看敏感数据的需求。而同一个人使用另一应用却不一定有业务上的正当理由来访问同样的数据。一个RESTful服务可以检查会话标志并且可以声明式的保证这一访问策略。这一层次上的明确性是需要的,以此才能防范内部欺骗,它通常是处理敏感内容系统的最大的威胁。这样一个系统的细节应当是特定实现的,并且与命名流程和解析逻辑命名的内容很大程度上是正交的。

依赖于逻辑关系能够保护客户不受实现变更的影响。当流行的网站从一种技术迁移到另一种技术,它们都能够成功地向用户隐藏这些变更。RESTful也是这么做的。这让我们可以自由地将遗留系统打包成逻辑接口并得以保留,直到因业务发展而投入新的实现。当出现这种情况的时候,用户也可以受到庇护而不受影响。

为了调停技术的变更,RESTful系统允许你使用Postel法则的一个变形:保守的给予,自由的接受。你可以对于你接受和返回的内容保持严格的验证。然而,如果你有一个现有的客户群向你提供某种给定形式的内容,你可以允许其它的客户提供不同的形式,不同的模式,而不影响现有的客户。与终端保持紧密关联合约的系统不可能提供这样的自由度,这将使他们更脆弱并且会很快瓦解。

超文本作为状态转移的引擎(HATEOS)

当系统遇到信息资源的引用时,很多人认为应当需要某种描述语言来指示什么是的可能或者应该做的。实际的情况是一个经过严密思考后的RESTful系统通常不需要这一概念。这对于SOAP开发者而言难以接受,但这是与架构风格的约束相关的。因为我们将信息资源看作是通过统一接口(URL)来操作的物件,并且将我们的行为局限着一个很小的动词集合里,因此确实没必要对服务进行描述。

如果你在这一点上有所疑惑,很可能是在架构风格的品位上你仍在操作资源与调用随意性行为之间产生了犹豫。REST动作对应用一个信息资源提供了全套的操作。当然,你需要知道返回的是什么信息你才能知道如何处理它,但这是MIME所关心的问题。尽管重用已知的类型(application/xml,image/png,等等)更为人所接受,但很多开发者并不明白如果需要的话他们可以创建自己的应用特定的数据类型。

在本系列文章的轨迹中,我们将讨论使用富元数据查找和绑定随意性资源的问题。现在,我们只需要简单的记住Roy所强调的“超文本作为状态转移引擎”的重要性(又被REST粉丝简称为“HATEOS”)。这恐怕是论文中引起最多误解的部分,要理解它的全部内涵,我们得重新过一遍Web是如何工作的。

你在浏览器中输入URL,它会向该资源发起一个HTTP GET请求。不变的是,服务器将响应一个字节流,一个响应代码(通常成功返回200),以及一个MIME类型以表示响应的是HTML。浏览器将决定它知道如何处理这一类型,并将结果解析成某种类型的文档模型。在这一模型中,可以找到对其它资源的引用:链接、图片、脚本、样式表等等。它会区别对待每一项,但都是在解析原始资源的过程中去发现它们的。这里不存在服务描述,浏览器作为客户端,直接就知道如何分析结果。

RESTful服务也应当使用同样的机制。URL本身不应当是“魔力的”。客户端不应当被要求了解如何去分析一个URL或是对于一个层次相对另一层次而言意味着什么有详细的认识。RESTful客户端只需要取回资源,调查返回的MIME类型并分析结果。这样一来,客户端就知道如何分析返回的类型了。

举例来说,一个客户端可能会接收一个我们上面所描述的用于报表服务的主RESTful服务的引用:

http://company1.com/report/

如果是通过浏览器发出的请求,可能会返回一个包含如下引用的HTML文档:

http://company1.com/report/sales

用户可以点击进入并找到可浏览的年份列表。要说明的是浏览器对于URL的结构并没有特别的认知,但它知道如何分析结果并以用户可以浏览的结果返回内容。

对于其它的MIME类型道理也是一样,比如以XML的格式请求2009年的季度报表:

http://company1.com/reports/sales/2009/qtr

可能得到:

<reports>
 <description>2009 Quarterly Reports</description>
 <report name="First Quarter" src="http://company1.com/reports/sales/2009/qtr/1"/>
 <report name="Second Quarter" src="http://company1.com/reports/sales/2009/qtr/2"/>
 <report name="Third Quarter" src="http://company1.com/reports/sales/2009/qtr/3"/> 
</reports>

你可以将URL想成是贯穿信息空间的向量。每一个层次都进一步的将你指向最终的资源。不同的路径可能产生同样的结果。客户端需要知道如何分析这些结果,但通过对响应给出可识别的类型,我们可以触发合适的分析器。这一结构可通过爬虫降序的从引用来抓取,或者以某种接口展现给用户浏览。一个RESTful接口成为了客户端通过基于已知来请求信息的方式。它们以一个已知或者已发现的点作为开始,就像你浏览web一样来浏览信息。

这就是HATEOS所指代的。应用的状态在超文本响应中被转移和发现。就像浏览器需要知道HTML、图片、声音文件等等一样,一个RESTful客户端也需要知道如何分析解析资源引用的结果。然而,整个过程是简单、受约束、可伸缩并且灵活的——这正是我们所期望的网络软件系统的属性。

许多人搭建的"RESTful"系统都要求客户端事先知道URL的每一层次的意义。如果服务端将信息重组了,这些系统的客户端就会崩溃。真正体现了HATEOS的客户端与它们所通讯的服务器之间才更加能做到松耦合。

展望

我们每天挣扎于解决由快速变更的领域、技术、客户需求以及行动知识等等方面引出的问题。我们花费了太多时间来编写软件,将我们所学到的与我们所已知的联系起来。对象与数据库没能够跟上我们所经历的变革。我们需要找到新的方式来看待我们所产生和消费的信息,比过去的解决方案更为扩展并且更加健壮。我们需要技术来帮助我们达成一致。我们不需要先达成了公共模型形式的一致才来使用我们的技术。

在这篇文章中,我们作了该系列的开篇介绍,并且开始探讨了REST与Web技术如何作为新的面向信息架构的基础。我们建立起了能够让我们统一引用各种内容,服务与文档的命名模式。客户端可以利用这样的自由度以他们想要的形式来协商信息。当他们解析引用的时候,他们可以发现通过新的关系联系起来的新内容。

这一架构风格和技术与相关的语义网技术结合起来,可以漂亮地创建强大的、可伸缩的和灵活的软件系统。它们创建数据Web的能力对我们的生活将产生的影响就像Web之前所产生的那样。这将会是一场信息系统的革命,我们的许多认知都将有很大程度上的改变。它不仅能降低数据集成的成本,还会使得我们只能进行想象的新型业务能力成为可能。

我们正在进入一个信息的新世界,信息可以被连接起来并使用,而不管它是被包含在文档内,数据库里,或者作为RESTful服务的结果而返回。我们将能够发现内容并将其连接到我们所已知的部分。我们可以将现在隐藏在数据库,电子表格,报表或者其它仓筒的数据暴露出来。我们不仅能获得访问这些信息的权限,还能够以我们想要的方式消费这些数据。

这是语义网的一个主要的,小小的目标。达到这个目标,而我们现在也有能力做到,将会是一切改变的开始。

查看英文原文Resource-Oriented Architecture: The Rest of REST

中文原文:http://www.infoq.com/cn/articles/roa-rest-of-rest

相关推荐