`
hideto
  • 浏览: 2650800 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

博文视点 & 软件世界

阅读更多
《RESTful Web Service》一书的样章试译审阅结果出来了,本人第一次正规翻译英文书籍,结果很多地方翻译错误或润色不够,最终没有通过审阅。从审阅的仔细程度来看,博文视点是非常注重翻译质量的,这是可喜之处。我试译的是第四章--面向资源架构的前半部分,翻译原文如下。
**********************RESTful Web Service**************************
第四章、面向资源架构
我已经向你展示了REST的威力,但是我还没有系统的向你展示这个威力是怎样组织的或者应该怎样暴露它。在本章我概述了一个具体的RESTful架构:面向资源架构(ROA)。ROA是一种将问题转变成RESTful Web服务的方式:一种像其他Web程序一样工作的URI、HTTP和XML的组织,程序员会喜欢使用这种方式。
在第一章我通过对两个问题的回答来将RESTful Web服务分类。这些答案对应于REST的四个特性定义中的两个:
  . 范围信息(“为什么服务器应该发送这个数据而不是那个数据?”)保存于URI。这是可寻址性原则。
  . 方法信息(“为什么服务器应该发送数据,而不是删除它?”)保存于HTTP方法。目前只有几个HTTP方法,而且之前人人都知道它们做什么。这是统一接口原则。
在这一章中,我介绍了面向资源架构的部件:资源(当然有它)、它们的名字、它们的呈现以及它们之间的联系。我解释并提倡ROA的特性:可寻址性、无状态性、连通性和统一接口。我说明了Web技术(HTTP,URI和XML)如何实现ROA部件从而使这些特性成为可能。
    在前面的章节中我通过已有的Web服务如S3来说明一些概念。在这一章我会继续发扬这一传统,但我也通过已有的网站来说明一些概念。希望我现在已经说服你网站是Web 服务,并且许多Web程序(例如搜索引擎)是RESTful Web服务。当我谈到一些抽象概念如可寻址性时,使用真实的URI来说明是非常有用的,你可以在Web浏览器里输入这些URI来实际了解这些概念。

面向资源然后呢?
  为什么又来一个新术语,面向资源架构?为什么不只是说REST?好,我确实在本书封面中说到REST,而且我说在面向资源架构中任何东西都是RESTful的。但是REST不是一个架构:它是一套设计标准。你可以说符合这些标准的一个架构比另一个好,但是没有一个“REST架构”。
到目前为止,人们趋向于在设计他们的服务时根据他们自己对REST的理解来构造一次性的架构。这样做最明显的结果是会产生大量创建者宣称是RESTful的REST-RPC混合Web服务架构。我正在尝试通过展示一些构建真正RESTful的Web服务的具体规则来阻止这种现象。在接下来的两章我将说明一些简单的过程,你可以按照这些过程来将需求转化为资源。如果你不喜欢我的规则,你至少要了解什么可以改变和什么需要保持RESTful。
作为一套设计标准,REST非常通用。特别是,它没有和Web绑定。REST没有任何东西依赖于HTTP机制或URI结构。但是我在说Web服务,所以我将面向资源架构和Web技术显式地绑定在一起。我想讲述怎样利用特殊的编程语言使用HTTP和URI来做REST。如果将来产生了不运行在Web上的RESTful架构,它们的最佳实践可能会看起来和ROA一样,但细节上会不同。我们遇到它时再说。
REST的传统定义留下了很多余地,这让许多实践者播下了民俗习惯的种子。我故意在Roy Fielding的论文里走的更深或在W3C的标准里走的更远:我想弄清楚这些余地以便民俗习惯有空间来成长为良好定义的最佳实践。即使REST是一个架构,用同样的名字来称呼我的架构是不公平的。我会将我富有经验的观察和建议与那些构建Web程序的人更一般的想法结合在一起。
我采用一个新术语最后的原因是“REST“是讨厌的宗教战争的一个术语。当使用它时,暗示着有一个真正的RESTful架构并且它是说话者的选择。选择另外一个RESTful架构的人们会不同意。部分REST社区对基本的东西如URI和HTTP的价值都达不成一致。
理想状况下没有宗教战争,但是我已经看到足够多战争以致我知道良好的愿望不会终结它们。所以我为我的RESTful程序应该怎样设计的哲学起一个突出的名字。当这些主意不可避免的用于战争的素材时,一般来说不同意我的人们可以陈述面向资源架构与其他RESTful架构以及REST不同的方面。透明度是理解的第一步。
“面向资源”和“面向资源架构”的定语通常用于描述RESTful架构*。我没有声称“面向资源架构”完全是一个最初的术语,但是我认为我的使用与先前已有的使用完全吻合,并且使用这个术语比整个宣称REST要好。

什么是资源?
资源是足够重要来让本身被引用为一个东西的任何东西。如果你的用户可能“想创建一个到它的超链接,获得或缓存一个它的展现,通过引用到另一个展现来包含它的所有或部分,标注它,或者在它基础上执行其他操作”,则你应该让它成为一个资源+。
通常,一个资源是可以存储在一台电脑上或作为比特流展示的东西:一个文档,数据库中的一行,或者运行一个算法的结果。一个资源可能为一个物理对象如一个苹果,或者一个抽象概念如勇气,但是(我们下面将看到)这些资源的展现必然令人失望。
这里是一些可能的资源:
. 软件发布的1.0.3版本
. 软件发布的最新版本
. 2006.10.4这天的第一个weblog entry
. Little Rock,Arkansas的地图
. 关于水母的一些信息
. 属于水母的资源目录
. 1024后面的下一个素数
. 1024后面的下5个素数
. Q42004的销售数字
. 两个熟人Alice和Bob的关系
. bug数据库里开放的bug列表

URI
是什么让资源成为资源?它至少得有一个URI。URI是资源的名字和地址。如果一段信息没有一个URI,则它不是一个资源并且不是真正在Web上,除了一些描述资源的数据。
*我所发现“面向资源”最早的实例是2004IBM developerWorks上Jame Snell的一篇文章:“Resource-oriented vs. activity-oriented Web services”(http://www-128.ibm.com/developerworks/
xml/libray/ws-restvsoap/)。本书发布之前Alex Bunardzic在2006年8月使用了“面向资源架构”:
http://jooto.com/blog/index.php/2006/08/08/replacing-service-oriented-architecture-with-resource-
oriented-architecture/。我并不同意这些文章里的所有东西,但是我承认它们对先使用该术语。
+“The Architecture of the World Wide Web”(http://www.w3.org/TR/2004/REC-webarch-20041215/
#p39)这篇文章充满了引证,附带了:“软件开发人员应该认为在程序间分享URI是有用的,即使最初这个功效并不显著”。这可以说是ROA战争的呐喊。
还记得在前言的例子里我取笑HTTP 0.9吗?让我们说这是http://www.example.com/hello.txt的一个HTTP 0.9请求:
Client request    Server response
GET /hello.txt    Hello, world!
一个HTTP客户端通过连接持有该资源的服务器(在这里为www.example.com)来操作资源,发送给服务器一个方法(“GET”)和一个该资源的路径(“/hello.txt”)。今天的HTTP 1.1比0.9稍微复杂一点,但是它以同样的方式工作。服务器和路径都来自于资源的URI。

Client request            Server response
GET /hello.txt HTTP/1.1   200 OK
Host: www.example.com   Content-Type: text/plain
                        Hello, world!
URI幕后的原则被Time Berners-Lee很好的描述为Universal Resource Identifiers----Axioms of Web Architecture(http://www.w3.org/DesignIssues/Axioms)。在这个部分我阐述了构建URI并将它们赋给资源的幕后原则。
URI是Web的基本技术。在HTML之前有超文本系统,有Internet协议,但是它们不能相互交流。URI将所有这些Internet协议连通到Web,即TCP/IP连接像Usenet、Bitnet和CompuServe的网络到一个单独的Internet的方式。然后Web吸取其他的协议并将它们统统除去,就像Internet对私有网络所做的一样。
如今我们在Web(不是Gopher)上冲浪,从Web(不是FTP站点)下载文件,从Web(不是WAIS)搜索出版物,并且在Web(不是Usenet新闻组)上谈话。我们使用在Web上工作的版本控制系统如Subversion和Arch,而不是自定义的CNS协议。甚至email也在慢慢转移到Web上。
Web清除掉其他的协议,因为它具有大部分协议缺乏的地方:一种标记任何可得到的item的简单方式。Web上的每个资源都至少有一个URI。你可以在告示牌上面粘贴一个URI。人们可以看到这个告示牌,输入该URI到他们的Web浏览器,然后进入到你想显示给他们的资源。这可能看起来有点奇怪,但是在URI被发明之前每天的这种交互都是不可能进行的。

URI应该是描述性的
这是ROA在REST论文里稀少的建议和W3C的建议上构建的第一点。我提倡一个资源和它的URI应该有一个直观的对应。这里是上面我列出的资源的一些良好的URI:
. http://www.example.com/software/releases/1.0.3.tar.gz
. http://www.example.com/software/releases/latest.tar.gz
. http://www.example.com/weblog/2006/10/24/0
. http://www.example.com/map/roads/USA/AR/Little_Rock
. http://www.example.com/wiki/Jellyfish
. http://www.example.com/search/Jellyfish
. http://www.example.com/nextprime/1024
. http://www.example.com/next-5-primes/1024
. http://www.example.com/sales/2004/Q4
. http://www.example.com/relationships/Alice;Bob
. http://www.example.com/bugs/by-state/open
URI应该有一个结构。它们应该在可预料的方式下各不相同:你不应该访问/search/Jellyfish来获得jellyfish或访问/i-want-to-know-about/Mice来获得mice。如果一个客户端知道服务的URI的结构,它可以创建它自己的到该服务的入口点。这让客户端以你没想到的方式使用你的服务更容易。
这不是REST的绝对规则,在后面的“给资源命名”部分我们会看到。URI在技术上不必具有任何结构或可预见性,但是我认为它们应该这样。这是良好的Web设计的一条规则,并且它类似地显露在RESTful和REST-RPC混合服务中。

URI和资源的关系
让我们考虑一些边缘情景。两个资源可以是一样的吗?两个URI可以指向同一资源吗?一个单独的URI可以指向两个不同的资源吗?
根据定义,两个资源不可以为一样的。如果它们是一样的,你可能只拥有一个资源。尽管如此,有时候两个不同的资源可能指向同样的数据。如果当前的软件版本为1.0.3,则这时http://www.example.com/software/releases/1.0.3.tar.gz和http://www.example.com/software/releases/
latest.tar.gz将指向同样的文件。但是这两个URI包含的意思却不同:一个一直指向一个特殊的版本,而另一个指向当前客户端可以访问的任何最新的版本。这是两个概念和两个资源。当版本1.0.3报告一个bug时你不能链接到最新的版本。
一个资源可能拥有一个或者多个URI。在http://www.example.com/sales/2004/Q4可以得到的销售数字也可能在http://www.example.com/sales/Q42004得到。如果一个资源有多个URI,则客户端会更容易得到资源。缺点是每个额外的URI削弱了所有其他URI的值。一个客户端使用一个URI,另外一些客户端使用另一个,并且没有自动验证所有的URI都指向同一资源的方式。
避免这个问题的一种方式是对同一资源暴露多个URI,但是让其中一个成为该资源的“标准”URI。当一个客户端请求标准URI,服务器发送合适的数据以及响应代号200(“OK”)。当一个客户端请求一个另外的URI时,服务器发送响应代号303(“See Also”)以及标准URI。客户端看不到两个URI是否指向同一资源,但是它可以发送两个HEAD请求并看到一个URI是否重定向到另一个URI或者它们是否都重定向到第三个URI。
另一种方式是提供所有的URI,看起来好像这些URI是一样的,但是无论何时有人请求一个非标准的URI都在Content-Location响应头部里给定“标准”URI
访问sales/2004/Q4可能让你得到和访问sales/Q42004一样的字节流,因为它们是同一资源不同的URI:“2004最后一个季度的销售”。访问releases/1.0.3.tar.gz可能让你得到访问realease/latest.tar.gz一样的字节流,但是它们是不同的资源,因为它们表示不同的东西:“版本1.0.3”和“最新的版本”。
每个URI精确的指向一个资源。如果它指向多个资源,它就不是一个Universal Resource Identifier。尽管如此,当你获取一个URI时服务器可能发送关于多个资源的信息:你请求的资源和其他相关的资源。当你获取一个Web页面时,它通常传输它自己的一些信息,但是它也拥有其他Web页面的链接。当你使用S3客户端获取一个S3记录时,你得到一个文档,该文档包含了关于该记录的信息和相关资源的信息:记录里的对象。

可寻址性
既然我介绍了资源和它们的URI,我可以深入ROA的两个特性:可寻址性和无状态性。
如果一个程序将它的数据集有趣的方面暴露为资源,则它是可寻址的。由于资源通过URI来暴露,则一个可寻址的程序对每个它想服务的信息片段暴露一个URI。这通常是无限多的URI。
从最终用户的角度来看,可寻址性是任何Web站点或程序最重要的方面。用户是聪明的,如果数据足够有趣,他们几乎会忽略或者绕开任何缺陷,但是很难绕开可寻址性缺陷。
考虑一个真实的URI,它以“关于水母的资源目录”的风格命名一个资源:http://www.google.com/search?q=jellyfish。这个水母的搜索就像一个真实的URI http://www.google.com一样。如果 HTTP不可寻址,或者如果Google搜索引擎不是一个可寻址的Web程序,我将不能在一本书里发布这个URI。我将不得不告诉你:“打开一个到google.com的Web连接,在搜索框里输入‘jellyfish’并点击‘Google Search’按钮”。
这不是一个理论上的担忧。直到上世纪90年代初期使用ftp://URI描述FTP站点文件流行起来,人们不得不像这样写到:“打开一个到ftp.example.com的匿名FTP会话,然后进入目录pub/files/并下载file.txt文件”。URI让FTP像HTTP一样可寻址。现在人们只需写:“下载ftp://ftp.example.com/pub/files/file.txt”。步骤是一样的,但是现在它们可以通过机器来执行。
但是HTTP和Google都是可寻址的,所以我可以在本书里打印该URI。你可以阅读它和输入它。当你这样做时,你最终到达我使用Google Web程序时所在的地方。
然后你可以将该页面加到书签并在稍后再次进入该页面。你可以在你自己的一个Web页面里链接到它。你可以email这个URI给任何其他人。如果HTTP不可寻址,你将不得不下载整个页面并将HTML文件作为一个附件发送。
为了节省带宽,你可以在你的本地网络建立一个HTTP代理缓存。第一次有人请求http://www.google.com/search?q=jellyfish时,缓存将保存该文档的一份本地副本。下次有人访问该URI时,缓存可能提供保存的副本而不是再去下载一次。这些东西只有在每个页面有一个独一无二的标识字符串(一个地址)时才有可能。
甚至可以将URI链接起来:使用一个URI作为另一个URI的输入。你可以使用一个外部Web服务来验证一个页面的HTML,或者将它的文本转换成另一种语言。这些Web服务期望一个URI作为输入。如果HTTP不可寻址,你将不能告诉它们你想让它们操作哪个资源。
Amazon的S3服务是可寻址的,因为每个记录和每个对象都有自己的URI,记录列表也是。不存在的记录和对象不是资源,但是它们也可以拥有自己的URI:你可以通过发送一个PUT请求到它的URI来创建一个资源。
你的家庭电脑上的文件系统是另外一个可寻址系统。命令行程序可以使用路径来访问一个文件并在文件上做一些奇怪的事情。电子表格的单元格也是可寻址的;你可以将一个单元格塞到一个公式里,然后公式将使用该单元格里的任何值。URI是Web上的文件路径和单元格地址。
可寻址性是关于Web程序最好的东西之一。它以最初设计者从未想到的方式让客户端更容易地使用Web站点。遵循这个规则会给你和你的用户许多REST的好处。这是为什么REST-RPC服务不常见的原因:它们将可寻址性和过程调用编程模型结合在了一起。这是我使用面向资源架构的名称给予资源最高赞誉的原因:因为资源是一种可寻址的东西。
这看起来很自然,这是Web应该工作的方式。不幸的是,许多Web程序不是这样工作的。特别是对Ajax程序尤为如此。我在第11章里说明了,大部分Ajax程序只是RESTful或混合Web服务的客户端。但是当你使用这些类似于Web站点的客户端时,你注意到它们感觉不像Web站点。
没有必要挑剔少部分人;让我们通过看看Gmail在线email服务来继续我们的Google服务旅程。从最终用户的角度来看,只有一个Gmail URI:https://mail.google.com/。不管你做什么,不管你从Gmail得到或者向Gmail上传任何信息,你将从来不会看到一个不同的URI。“关于水母的email消息”这个资源不可寻址,Google的“关于水母的Web页面”的方式是这样++。而我在第11章讲到,在幕后,它是一个可寻址的Web站点。关于水母的emial消息列表实际上有一个URI:https://mail.google.com/mail/?q=jellyfish&search=query&view=tl。问题是,你不是这个Web站点的消费者。这个Web站点不是一个真正的Web服务,并且真正的消费者是一个运行在你的Web浏览器里的JavaScript程序$。Gmail Web服务是可寻址的,但是使用它的Gmail Web程序不可寻址。

无状态性
寻址性是ROA的四个主要特性之一。第二个是无状态。我将给你两个无状态的定义:一个稍微普通的定义和一个更面向ROA实践的定义
无状态性意味着每个HTTP请求发生于完全隔离的状况下。当客户端执行一个HTTP请求时,它包含服务器用来满足该请求所有必要的信息。服务器从不依赖于先前请求的信息。如果信息是重要的,客户端将在请求中再发送一次。
++比较一下Ajax界面和通过使用https://mail.google.com/mail/?ui=html这个URI的更有可寻址性版本的Gmail。如果你使用这个简单的HTML界面,则“关于水母的email消息”是可寻址的。
$这个Web服务的其他消费者包括Python的libgmail库(http://libgmail.sourceforge.net/)
更加从实际出发一点,考虑从可寻址性的角度考虑无状态性。可寻址性说服务器可提供的信息的每个有趣的部分都应当暴露为资源,并且赋予它自己的URI。无状态性说服务器可能的状态也是资源,并且应该给予它们自己的URI。客户端不应该哄骗服务器到一个特定的状态来让它接受一个特定的请求。
在现实Web中,你经常陷入到你的浏览器的后退按钮不能正常工作的处境,并且你不能在你的浏览器历史里后退或前进。有时候这是因为你执行了一个不可废除的动作,例如发布一个weblog entry或者购买一本书,但是通常这是因为你在一个违反无状态原则的Web站点上。例如一个希望你按某一顺序如A、B然后C执行请求的站点。当你第二次执行请求B而不是继续请求C时站点会被迷惑。
让我们再来看看搜索的例子。一个搜索引擎是一个拥有无限多可能的状态的Web服务:至少对于每个你可能搜索的字符串有一个状态。每个状态都有自己的URI。你可以请求该服务获得一个关于老鼠的资源目录:http://www.google.com/search?q=mice。你可以请求一个关于水母的资源目录:http://www.google.com/search?q=jellyfish。如果你不喜欢对从零开始创建一个URI,你可以请求服务用一种统一的形式来满足:http://www.google.com/。
当你请求一个关于老鼠或水母的资源目录时,你不会得到整个目录。你得到该目录的一个单独的页面:一个搜索引擎认为最满足你的查询的大约10个items的列表。为了得到更多目录你必须执行更多的HTTP请求。第二个和后续的请求页面是程序的不同状态,并且它们需要有自己的URI:类似于的http://www.google.com/search?q=jellyfish&start=10东西。使用任何可寻址的资源,你可以将程序的状态转移到另外的状态,缓存它,把它加入书签并在稍后再回到它。
图4-1是一个显示一个HTTP客户端可能怎样与一个搜索引擎的四种状态交互的状态图。
这是一个无状态程序,因为每次客户端执行一个请求时,它最终回到它开始的地方。每个请求与其他请求是完全分离的。客户端可以以任何顺序对这些资源执行任何次数的请求。它可以在请求“老鼠”的页面1之前请求2(或者根本不请求页面1),服务器并不在意。
作为对比,图4-2说明了一些被安排为有状态的情形,每种情形都对状态敏感。大部分桌面程序都是以这种方式设计的。
这样组织稍微好一些,如果HTTP被设计为允许有状态的交互,HTTP请求可以更简单。当客户端对搜索引擎启动一个会话则它的状态可以被搜索表单自动填充。它根本不必发送任何请求数据,因为第一个响应是预先注定的。如果客户端正在老鼠目录查看前10项entry并且想看到11-20的entry,它可以只发送一个说明“start=10”的请求。它不必发送/search?q=mice&start=10来重复最初的声明:“我在搜索,并且特别的,我在搜索老鼠”。
图4-1一个无状态搜索引擎
图4-2一个有状态搜索引擎
FTP这样工作:它有一个“工作目录”的概念来在整个会话过程中保持不变,除非你更改了它。你可以登录到一个FTP服务器,cd到某一个目录,然后从该目录get一个文件。你可以在同一目录get另外一个文件而不必再使用cd命令。为什么HTTP不支持这个呢?
状态可以让个别HTTP请求变简单,但是它会使HTTP协议变得更复杂。一个FTP客户端比一个HTTP客户端更复杂,因为会话状态必须在客户端和服务器之间保持同步。即使对于一个可靠的网络来说这也是一个复杂的任务,Internet是不可靠的网络。
从协议上消除状态将消除许多失败的状况。服务器从来不需担心客户端超时,因为没有一个交互比一个单独的请求持续的更久。服务器从不丢失对每个客户端在程序“哪里”的跟踪,因为客户端的每次请求都发送所有必要的信息。客户端从不会因为服务器保持某种状态并且没有告诉它而以在错误的“工作目录”执行一个动作告终。
**********************RESTful Web Service**************************

另外最近给《软件世界》杂志写了篇关于RESTful框架的文章,将在9月份发表
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics