以文本方式查看主题 - W3CHINA.ORG讨论区 - 语义网·描述逻辑·本体·RDF·OWL (http://bbs.xml.org.cn/index.asp) -- 『 Web Services & Semantic Web Services 』 (http://bbs.xml.org.cn/list.asp?boardid=10) ---- 《RESTful Web Services 中文版》连载 —— 《前言》 (http://bbs.xml.org.cn/dispbbs.asp?boardid=10&rootid=&id=62708) |
-- 作者:admin -- 发布时间:5/16/2008 5:54:00 PM -- 《RESTful Web Services 中文版》连载 —— 《前言》 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ 前言 复杂的系统总是由简单的系统演变而来的。 —John Gall Systemantics 我们写这本书,是要告诉你一项令人瞩目的新技术。喏,它很热门,它会彻底改变我们编写分布式系统的方式。我们要讲的是万维网(World Wide Web,简称Web)。 没错,Web不是什么新技术,也不如昔日那么火了,而且从技术角度来看,它并不是那么令人瞩目。但它的确改变了我们许多。这10年来,Web已经改变了我们生活的方式;不过,更多潜在的改变将等待我们。Web是简单的、无所不在的;然而,它作为分布式编程平台的潜力却被忽视了。我们编写本书的目的,就是要让大家体验Web的这种潜力。 说Web作为分布式编程平台的潜力被忽视了,这听上去也许令人感到诧异。毕竟,本书还要与其他Web服务相关书籍竞争。问题是,大部分如今的 “Web服务”都与Web毫无干系。它们采用像COM、CORBA那样的重量级分布式对象访问架构——这与Web的简单性背道而驰。如今的“Web服务” 架构重复或忽略了Web赖以成功的每一种特性。 其实并不需要那样。Web背后的(underlying)技术足以支撑强大的远程服务——其实那些服务就存在着,而且我们每天都在使用它们。这种服务可以延伸到巨大的规模——其实这已经实现了。以Google搜索引擎为例,它不就是一个对海量数据库进行查询并返回结构化搜索结果的远程服务吗?通常,我们不把网站(web site)当作“服务(service)”来看,因为网站的最终用户是人,而服务是程序之间的对话。但网站就是服务。 每个Web应用(包括每个网站)都是一个服务(service)。只要遵从Web的理念而不是违反它,只要不把Web特有的能力隔离在很多层抽象之下,你就能够让可编程应用(programmable applications)利用这种能力。现在是让“Web服务”回归“Web”理念的时候了。 令网站易于被上网者使用的那些特性,同样也令Web服务API易于被程序员所使用。为寻找服务的设计原则,我们可以从网站的设计原则着手,并思考如果网站的使用者不是人而是程序,那最终将得到什么样的设计原则。 本书就是这么做的。我们的中心目标,就是展示Web基础技术(HTTP应用协议、URL命名标准、XML标记语言)的强大能力、适用场合及局限等。本书主要讲的是Web背后的(underlying)一套设计原则——表示性状态转移(Representational State Transfer),或简称为REST。我们率先为“REST式(RESTful)”Web服务提出了最佳实践(best practices)。我们不会采用含糊或臆断性的语言,相反,将用具体的建议来取代那些坊间传言(folklore)和隐性知识。 我们引入了面向资源的架构(Resource-Oriented Architecture,ROA)作为用于设计REST式Web服务(RESTful web services)的一组切合实际的原则。我们还会教你如何编写客户端程序来调用REST式服务。我们将采用一些真实的REST式服务作为案例,比如: Amazon S3(Simple Storage Service)、各种Atom发布协议的变形,以及Google Maps等。我们也会举一些流行的但不符合REST原则的例子(比如 del.icio.us 的社会性书签API),然后对它们进行重构。 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ 简单的Web 为何我们对Web如此着迷,以至于认为它能解决所有问题?也许我们上当了,成为炒作的受害者。尽管HTTP并不是最受欢迎的 Internet协议,但Web无疑是被炒作得最凶的一种Internet技术。据统计,全球Internet流量中的大部分源自电子邮件(归因于垃圾邮件)或BitTorrent(归因于侵犯版权)。假如明天Internet就不复存在,那么人们最为怀念的将是电子邮件。那为何要如此重视Web呢?是什么使得HTTP——一种为物理实验室之间传递研究记录而设计的协议——同时能够适合分布式Internet应用呢? 实际上,说HTTP是为某某目的而设计的,对它是一种极大的恭维。已经有人说了,HTTP与HTML是“Internet协议里的放屁坐垫(Whoopee Cushion)与欢乐蜂鸣器(Joy Buzzer),只能搞些小把戏”(译注1)——这还是一个喜爱它们的人说的。(注1)第一版HTTP(即HTTP 0.9)的确看上去像是搞小把戏,比如下面这个客户端与服务器交互的例子: 就这么简单。你连接到服务器,把文档路径给它,然后服务器就把文档内容返回给你。HTTP 0.9 差不多就只支持这么些了,看似只是毫无特色地照搬比它复杂一点的文件传输协议(FTP)。 HTTP 0.9是特地设计成那么简单的。从HTTP 0.9中我们可以看到可寻址性(addressability)和无状态性(statelessness)——正是这两条基本设计原则,令HTTP较其同类更加优秀,并得以延伸到今天如此巨大的规模。在HTTP 0.9所缺少的特性中,大部分已被证实是多余的,甚至是有副作用的(其实,添加那些特性将有损于Web),而其余的许多特性已在HTTP 1.0和1.1版中实现了。Web赖以成功的另两项重要技术是URL和HTML(以及后来的XML)——它们在许多重要方面也是简单的。 显然,这些“简单的”技术在功能上已足够强大,它们支持Web及Web上的应用。在本书中,我们进一步提出:万维网(World Wide Web)是一个简单而灵活的分布式编程环境。而且我们认为其原因是:为人类使用而设计的human web,跟为软件程序调用而设计的“programmable web”没有本质区别。我们认为,如果Web能很好地为人类所用,那么它也同样能很好地为程序所用。我们只要多作一些考虑即可。计算机程序擅长于构建和解析复杂的数据结构,但是在理解文档方面,它们无法做到像人类一样灵活。 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ 用作构建Web服务的协议与标准有很多,它们大多是构筑在HTTP之上的。这些标准被统称为WS-*标准栈。它们包括WS- Notification、WS-Security、WSDL及SOAP等。在本书中,我们用“大Web服务(Big Web Services)”来称呼这些技术,以较为礼貌地表达我们对它的鄙视。 对于某些WS-*标准(例如SOAP),你可以做到使用它们,而不违反REST及面向资源的架构(Resource-Oriented Architecture)。但实际上,它们被用于实现基于HTTP的远程过程调用(Remote Procedure Call,RPC)。有时,采用RPC式架构是合适的;有时,有比Web的优点更为重要的需求得考虑。这没关系。 不必要的复杂性是我们所不期望的。有些场合,采用普通老式的HTTP(plain old HTTP)就足以应付了,但程序员或公司却经常采用大Web服务(Big Web Services)。这样做的效果是,HTTP成为一种用于传输庞大XML负载(payload)的协议,而“真正的”描述信息在XML里。最终的服务变得太过复杂、难于调试,而且要求客户端必须与服务端具备同样的配置环境。 大Web服务(Big Web Services)确实有个优点:现今的开发工具使你只需点一下鼠标,就可以根据代码生成Web服务(尤其是Java或C#开发)。在用这些工具来生成符合WS-*标准栈的RPC式Web服务时,REST式Web服务的简单性这一优点对你来说也许已经不重要了,因为这些工具已经隐藏了所有的复杂性,不用让你操心。毕竟带宽和CPU都便宜。 假如是在同质(homogeneous)环境中,且服务都在同一个防火墙之后的话,那么这种做法是可行的。如果你的机构有足够的政治影响力,可以要求防火墙外的其他机构也采用跟你们同样的方式。但是,假如期望服务逐渐发展到Internet规模,就必须能够应付没有事先考虑到的客户端,比如采用你从没想到的自定义软件栈(software stacks)来访问服务。用户会试图把你的服务与其他你没听说过的服务集成起来。貌似很难?这在Web上已经司空见惯了。 抽象(abstraction)也不是完美的。每增加一个层次,就多一些故障点(failure point)、互操作难题和可伸缩性问题。现代化开发工具能够隐藏复杂性,但它们无法为引入复杂性解释原因——它们就知道不断地增加层次。要令服务融入 Web,就要在适应性(adaptability)、可伸缩性(scalability)及可维护性(maintainability)方面多加注意。而简单性(simplicity)——HTTP 0.9被忽视的优点——则是这三者的先决条件。系统越复杂,出错时纠正起来就越困难。 如果你提供REST式Web服务,可以把复杂性投入“实现更多特性、实现不同服务之间的交互”中。成功地提供服务,意味着不光把服务构筑在Web “之上”,而且应该令服务融入Web:设计服务时,应遵循跟“良好的网站设计所采用的”一样的原则。跟基本的Web协议靠得越拢,这就越容易实现。 关于REST REST虽然简单,但它是良好定义的(well-defined);而且,不能因为“Web服务和网站是一样的东西”,就以简单性作为“把 Web服务实现成功能贫乏的网站”的借口。可惜到目前为止,主要的REST参考文献只有Roy Fielding 2000年的博士论文第5章——就博士论文来说,那是很好的资料;不过对于大部分现实问题,它并没有给出回答。(注3)这是因为,那篇博士论文不是把 REST作为一种架构来诠释,而是把REST当作一种评判架构的方法来介绍的。“REST式(RESTful)”这个术语就像“面向对象(object- oriented)”这个术语一样。一门语言、一种框架或一个应用也许是采用面向对象的方法设计的,但这并不一定确保其架构是面向对象的架构。(注3) 即使采用像C++或Ruby这样面向对象语言,也有可能写出不是真正面向对象的程序出来。按照REST的标准,HTTP在理论上是很好的。(这是理所当然的,因为Fielding参与了HTTP标准的编写,而且他在博士论文里描述了Web的架构。)不过,在现实中,网站、Web应用及Web服务等经常与REST原则相违背。那么,在对具体的Web服务进行设计时,你怎样才能确信自己正确运用了REST原则呢? 大部分关于REST的信息来源都是非正式的:邮件列表(mailing list)、Wiki、博客等(我在附录A中给出了一些很好的链接)。直至如今,REST的最佳实践(best practices)还只是一些坊间传言(folklore)。所需要的,是一个以REST元架构(meta-architecture)为基础的具体架构(architecture):一套为设计“体现了Web的潜力”的服务而制定的基本准则——我们将在第4章介绍这样一种架构,即面向资源的架构(Resource-Oriented Architecture,简称ROA)。它并不是唯一的高层(high-level)REST式架构,但是我们认为,用它来设计“易于为客户端所用”的 Web服务是很好的。 我们通过制定这个面向资源的架构(ROA),把来自坊间传言(folklore)的经验提炼为Web服务设计的最佳实践(best practices)。它们仅作为一个建议的基线(suggested baseline)。如果你曾经力图领会REST,希望我们的架构能够令你自信地认为所做的是“真正的”REST。我们也希望,面向资源的架构(ROA)能够帮助整个社区加快提出并系统化最佳实践我们希望让程序员可以轻易地构建优雅的、符合设计用途的、融入Web的(而不是仅仅构筑在Web之上的)分布式 Web应用。 但我们知道,你光掌握这些技术还是不够的。我们两人都曾有过这样的经历:所工作的机构,在主要的架构决策上没有按照我们的意思去做。如果没机会采用 REST式架构,你是无法在REST式架构方面取得成功的。因为,除了技术知识以外,我们还必须教你怎样用言辞去推荐REST式解决方案。我们已经把面向资源的架构(ROA)定位为一种替代RPC式架构(即如今的SOAP+WSDL服务所采用的)的简单方案。RPC式架构通过一个复杂的、编程语言式的接口,来暴露其内部算法(algorithms)——这种接口,每个服务都各不相同。而ROA则通过一个简单的、文档处理接口,来暴露其内部数据(data)——这种接口是统一的。我们将在第10章对这两种架构加以比较,并教你如何来推荐采用ROA。 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ 统一两种Web 程序员把网站作为Web服务来用,这已经有好多年了——当然,是非正式的。(注4)要让计算机理解供人类阅读的网页,是比较困难的,但这并没有阻止程序高手们用自动化客户端来抓取网页,然后用屏幕抓取程序来提取有关信息。随着时间的推进,出现了RSS、XML-RPC和SOAP这些对程序员友好的技术——它们用于按正式认可的方式来暴露网站功能。这些技术形成了一种programmable web,它是对human web的扩展,它为软件程序提供了方便。 我们写这本书的最终目的,是统一programmable web与human web。我们设想的是单个互联的网络:一个“在一组服务器上运行,采用一套协议并遵从一组设计原则”的万维网(World Wide Web)。那是一个既可被人也可被计算机程序使用的网络。 若不是得益于分配不当的国防资金、创新的工程项目、“差点则更好”(worse-is-better)的工程实践、大科学、天真的自由理想主义、古怪的自由主义政治、技术迷信,以及那些认为自己找到了发财之路的程序员和投资者的汗水与资本,因特网(Internet)和万维网(World Wide Web)也许不一定会诞生。 Web作为一个简单的、开放的(至少现阶段)和基本统一的网络应用平台,它容纳了大量的人类知识,并为人类所努力钻研的许多领域提供支持。我们认为,现在应该开始认真考虑将网站设计原则应用于服务设计,以便自动化客户端可以访问那些信息和算法。假如同意的话,本书将教你怎么做。 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ 本书的内容 在本书中,我们主要关注一些实际问题:如何设计与实现REST式Web服务(RESTful Web Services),以及调用它们的客户端。其次我们还关注于理论方面:什么是REST式架构风格?为什么Web服务应该尽量符合REST风格?我们的讨论不会涵盖方方面面,但是力图切入如今的重点话题。因为这是同类书中的第一本,我们会不断反复这一关键问题——即如何设计一个REST式服务。 前3章,将从客户端的角度来介绍Web服务,并告诉你REST式服务有什么特别不同。第1章 Programmable Web及其分类 第2章 编写Web服务客户端 在这一章,我们教你如何用HTTP库和XML解析器来为现有Web服务编写客户端。我们将介绍一个流行的REST-RPC服务—— del.icio.us Web服务,并用Ruby、Python、Java、C#及PHP等语言来示范其客户端代码。对于其他一些语言,我们虽然没有展示代码,但会推荐一些适用的HTTP库和XML解析器。JavaScript和Ajax将在第11章中作单独介绍。 第3章 REST式服务有什么特别不同? 我们将吸取第2章的经验,并把这些经验运用于一个纯REST式服务——Amazon S3(Simple Storage Service)。我们将在构建S3客户端时举例说明一些重要的REST概念:资源(resource)、表示(representation)、统一接口(uniform interface)。 第4章 面向资源的架构 本章正式介绍REST:不是抽象地介绍,而是以一个具体的Web服务架构为背景进行介绍。我们的架构基于四个重要的REST概念:资源、资源名称、资源表示(representation),以及资源间的链接。它的服务应根据四个REST特征来评判:可寻址性、无状态性、连通性和统一接口。 第5章 设计只读的面向资源的服务 我们提出了“把想法或需求转变为REST式资源”的系列步骤。这些资源是只读的,也就是说客户端可以从服务获取数据,但是不能改变服务的数据。我们以一个设计地图服务(类似Google Maps)的例子来说明这些步骤。 第6章 设计可读写的面向资源的服务 我们扩展了上一章的步骤,以允许客户端创建、修改并删除资源。我们以为地图服务新增两种资源(用户账户和自定义地点)为例,示范这些步骤。 第7章 一个服务实现 我们把一个RPC式服务(即第2章那个del.icio.us REST-RPC混合服务)重构为一个纯REST式服务。然后,我们把这个服务实现为一个Ruby on Rails应用。通过这一章,你可以把所学到的知识操练一遍! 第8章 REST和ROA最佳实践 在这一章,我们把之前给出的关于服务设计的建议汇集起来,并补充一些新的建议。我们将告诉你如何利用HTTP的标准特性来解决常见问题和进行优化。我们还将为棘手的特性(比如事务,也许你认为这没法用REST式Web服务来实现)给出面向资源的设计。 第9章 服务的技术构件 我们在这一章描述在REST的三大技术(HTTP、URI和XML)之上的其他技术:有些是用于传递状态的文档格式,比如XHTML及其微格式(microformat);有些是用于为客户端状态推进的超媒体格式,比如WADL;有些是用于构建REST式Web服务的控制流,比如Atom发布协议。 第10章 面向资源的架构 VS 大Web服务 将拿我们的架构及一般的REST架构跟其他著名架构来进行比较。我们认为REST式Web服务较“基于SOAP、WSDL和WS-*”的服务而言,更为简单、可伸缩性更好、更易于使用、更加符合Web的理念,且更能应付各种各样的客户端。 第11章 将Ajax应用作为REST客户端 在这一章,我们将以Web服务来阐述用于Web应用的Ajax架构:一个Ajax应用,就是一个在浏览器里运行的Web服务客户端。本章是对第2章内容的延伸。我们教你如何用XMLHttpRequest和标准的JavaScript库来编写用于REST式Web服务的客户端。 第12章 REST式服务框架 在最后这一章里,我们将讨论三种流行的框架:Ruby on Rails、Restlet(用于Java)和Django(用于Python)。它们可以简化REST式Web服务的开发。 我们还有3个附录,希望对你有用。 附录A REST相关资源与REST式资源 第一部分列出了一些与REST式Web服务有关的标准、教程和社区。第二部分给出了一些现有的、公开的、可供你调用和学习的REST式Web服务。 附录B 42种常见的HTTP响应代码 这部分解释标准HTTP的每一个响应代码(以及一个扩展),并解释何时你会在REST式Web服务里用到这些代码。 附录C 常见的HTTP报头 这部分向你解释各种HTTP报头,包括每个标准的HTTP报头,以及一些用于Web服务的扩展报头。 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ 你应该阅读哪些部分? 本书的组织,适于那些对Web服务的概况感兴趣的读者:有些读者通过实践学习Web服务,但他们对于Web服务没有太多经验。如果你属于这种情况,可以采取一条最简化的阅读路线,即从本书开头一直读到第9章,然后在剩下的章节中选择你所感兴趣的部分。 如果你经验比较丰富,那么可以采取另一种阅读路线。如果你关心如何为现有服务编写客户端,那么可以只关注第1、2、3和11章——那些关于服务设计的章节对你可能帮助不大。如果你想要创建自己的Web服务,或者想知道REST到底是什么意思,你不妨从第3章开始阅读。如果你想比较REST与WS- *,那么你可以选第1、3、4和10章作为起点。 说明 本书有两位作者(Leonard和Sam),在本书的1-11章,我们将把这两个身份合并为第一人称“我”。而在最后一章(第12章)里,这个第一人称“我”代表的是一大群人,因为有众多Django和Restlet开发者们参与到“展示如何用他们的框架来构建REST式服务”中来。 我们假定你是一名胜任的程序员,但不假定你在Web编程方面有任何经验。我们在书中介绍的内容并不限定于某一编程语言。我们提供了以各种语言编写的REST式客户端及服务的示例代码。除非为了示范特定的框架或语言,否则我们将采用Ruby(http://www.ruby-lang.org)语言来描述。 我们选用Ruby是因为:即使对于不懂Ruby的程序员来说,它也是简洁易读的。(因为这是一门很好的语言,而且它的名字跟Sam的姓有着令人困惑的联系。)Ruby的标准Web框架——Ruby on Rails——也是最主要的一种REST式Web服务实现平台之一。如果你不懂Ruby也没关系,我们在代码中嵌入了许多有助于理解Ruby语法的注释。 本书的示例程序可以从本书官方网站([URL=http://www.oreilly.com/catalog/9780596529260]http://www.oreilly.com/catalog/9780596529260[/URL])上下载,其中包括第7章的Rails应用的完整代码,以及第12章中Restlet和Django应用的相应代码。在书中,许多客户端我们只给出了 Ruby实现;但在示例程序中,你还可以找到它们的Java实现。这些客户端程序采用了Restlet库,并且是由Restlet开发者Jerome Louvel和Dave Pawson编写的。若相对Ruby而言,你更熟悉Java,那么这些代码对你掌握代码背后的概念会有帮助。特别值得注意的是,示例代码里还包括一个完整的第3章Amazon S3客户端的Java实现。 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ 致谢 最后,我们要感谢那些令我们得以直接在HTTP上编程的人们。对于Sam来说,是Rael Dornfest和他的Blosxom博客应用。Leonard的经验来自于20世纪90年代中期编写屏幕抓取应用。他感谢那些把网站作为Web服务来设计的人们,特别是那位在线连环画“Pokey the Penguin”的匿名作者。 我们曾有过这样的想法,不过是Roy Fielding充实了它。对于我们,那只是一种感觉,而Roy Fielding则在他的博士论文里命名并定义了它。Roy的理论基础正是我们所赖以为基础的。 在本书编写过程中,我们从REST社区获得了巨大的帮助。我们非常感激给我们反馈的人:Benjamin Carlyle、David Gourley、Joe Gregorio、Marc Hadley、Chuck Hinson、Pete Lacey、Larry Liberto、Benjamin Pollack、Aron Roberts、Richard Walker和Yohei Yamamoto。还有一些人通过他们的作品在不知不觉中帮助我们:Mark Baker、Tim Berners-Lee、Alex Bunardzic、Duncan Cragg、David Heinemeier Hansson、Ian Hickson、Mark Nottingham、Koranteng Ofosu-Amaah、Uche Ogbuji、Mark Pilgrim、Paul Prescod、Clay Shirky、Brian Totty和Jon Udell。当然,书中的所有看法及可能存在的错误与疏漏都是我们自己的责任。 在本书编写过程中,编辑Michael Loukides为我们提供了很多帮助与智慧。我们还要感谢Laurel Ruma和其他所有为本书出版付出劳动的O’Reilly同事们。 最后,需要特别感谢的是Jerome Louvel、Dave Pawson和Jacob Kaplan-Moss。他们在Restlet和Django方面的经验令我们有了本书的第12章。 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/
[此贴子已经被作者于2008-5-16 19:32:37编辑过]
|
-- 作者:admin -- 发布时间:5/16/2008 6:42:00 PM -- 《RESTful Web Services 中文版》连载 —— 《第一章 Programmable Web的分类》 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ 第1章 Programmable Web及其分类 在编写计算机程序时,并非只能使用自己设计的算法,你还可以使用编程语言标准库里的算法;另外,也可以从相关图书或第三方库里找到更多的算法。除非在做非常前沿的东西,否则一般不必自己设计算法。 运气好的话,数据也能找到现成的。某些应用是完全由用户所提供数据驱动的。有时,获得数据是轻而易举的。比方说,假如要对垃圾邮件进行分析,你很容易获得所需数据:你可以下载一些公共数据集——单词表、地理数据、素数表、公共领域文本(public domain texts)等——它们就像第三方库一样供你使用。但是,假如你需要其他种类的数据,那就不是这么容易了。哪里有这些数据呢?它们越来越多地来自于 programmable web(译注 )。 作为人类用户,当你要寻找有关某话题的图书时,多半会用浏览器打开一个在线图书馆或在线书店的URI,比方说[URL=http://www.amazon.com/]http://www.amazon.com/[/URL]。 提示:“URL”是一个常被用来表达网址的术语。但在本书中,我将遵从HTTP标准,统一采用“URI”这一术语。因为每个作网址用的URI也是一个URL,所以你可以用“URL”来替换我说的“URI”,它们是一个意思。 你将收到服务器返回的一个网页,即一个HTML格式的文档,浏览器将把该HTML文档以图形化的方式展示在你面前。你会看到该网页里包含一个搜索表单(form),可以在其中输入要搜索的主题(比如“web services”),然后提交该表单。此时,浏览器将再次发出HTTP请求,这次请求的目标URI将提供你所寻找的主题的搜索结果。继续Amazon的例子,浏览器第二次请求的目标URI可能是这样一个URI:[URL=http://amazon.com/s]http://amazon.com/s[/URL]? url=search-alias%3Dstripbooks&field-keywords=web+services。 示例1-1:amazon.com返回的HTML文档(片断) ... programmable web也差不多,它跟human web的主要不同在于:programmable web返回的不是令人赏心悦目的HTML页面,而是冷漠刻板的XML文档。programmable web不是专门面向人类用户使用的,它的数据是准备作为“软件程序的输入”用的。 示例1-2是一段Ruby脚本,它用programmable web来完成一项传统的human web的任务:搜索所有匹配某个关键字的图书。它通过采用Ruby/Amazon库([URL=http://www.caliban.org/]http://www.caliban.org/[/URL] ruby/ruby-amazon.shtml)将访问Web的细节隐藏在编程语言接口之后。 示例1-2:一段用于搜索图书的Ruby脚本 #!/usr/bin/ruby -w if ARGV.size != 2 示例1-3:xml.amazon.com返回的XML文档(片断) ... 对Web浏览器来说,它的职责只是发出HTTP请求,然后以一种人类可以理解的方式把服务器的响应呈现出来。它不必思考这个HTTP响应是什么意思 ——那是人类用户的事。而Web服务客户端就没这么轻松了:因为程序是事先编好的,所以它必须既像Web浏览器一样获取数据,还要像“人类用户”一样,判断这些数据是什么意思;Web服务客户端必须自动地从HTTP响应中提取含义,并根据含义决定下一个动作。 示例1-2所示的Web服务客户端先解析XML文档,提取一些相关信息(书名及作者),然后把这些信息在标准输出(standard output)上打印出来。实际上,amazon-book-search.rb这个程序就像一个专用的小型Web浏览器,把数据传递给人类用户。它也可以在无人工干预的情况下,容易地对Amazon的图书数据做一些其他操作,比如:把书名存入数据库,或者以作者信息来驱动一个推荐引擎(recommendation engine)等。 示例1-4显示的是一个修改programmable web的Web服务客户端:Ruby的s3sh命令行解释器(command shell)([URL=http://amazon.rubyforge.org/]http://amazon.rubyforge.org/[/URL])。它是Amazon S3(Simple Storage Service)([URL=http://aws.amazon.com/s3]http://aws.amazon.com/s3[/URL])的各种客户端中的一个。我会在第3章详细介绍S3的工作原理。如果你想学习使用s3sh的话,到时可以仔细研究。 你只要知道Amazon S3的作用是“在桶(一种带标签的容器)里存放对象(一种带标签的数据)”,就不难理解这个s3sh脚本了。s3sh程序在S3之上构建了一个交互式编程接口,而其他一些客户端则把S3用于备份或Web寄存。S3是一个非常灵活的服务。 示例1-4:用s3sh和S3处理programmable web $ s3sh >> my_bucket = Bucket.find("example.com") >> contents = open("disk_file.txt").read >> S3Object.store("mydir/mydocument.txt", contents, my_bucket.name) >> my_bucket['directory/document.txt'].value 本章将对programmable web的现状作一个概述:它用到哪些技术,这些技术采用什么架构,以及哪些设计风格比较流行?我会展示一些实际代码及实际HTTP交互的例子,但本章的主要目的,是希望你将万维网(World Wide Web)看成一种把计算机程序彼此衔接起来的方式,就如同使彼此连系起来一样。 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ Programmable Web的分类 programmable web是基于HTTP和XML技术的。虽然HTML、JSON(JavaScript Object Notation)、纯文本或二进制文档也在使用,但是采用较多的还是XML。programmable web还是基于HTTP的:不采用HTTP,就不是基于Web的(注1)。除了这些仅有的共识,剩下的就差不多是争论了。没有固定的术语,而且同一术语(就像“REST”)各人有各人的用法,让人感到含糊和困惑。目前所需要的是一种对programmable web进行统一分类的方法,有了这样的分类,各个术语的含义就清晰化了。 这两种对海洋生物的分类方法常常和谐相处,没必要通过DNA检测来确定金枪鱼是更像鲶科鱼,还是更像海葵。但是,假如你很想知道为什么鲸在水下不能呼吸的话,你就不能(根据外表)把它归为鱼类,而应(根据内在的构造)将它归为哺乳类(注2)。 就programmable web的分类来说,目前的术语,大部分是根据外表(即所采用的技术)来对服务进行分类的。虽然在大多数情况下没有问题,但它们存在概念上的缺陷,而且会导致“鲸是鱼”的错误。我将提出一种基于架构的分类,并说明如何根据背后的设计原则作技术选择。我会在书中不断提及这里给出的分类,但我主要关心的是那些 programmable web里跟“REST”有一定联系的部分。
[此贴子已经被作者于2008-5-16 19:37:02编辑过]
|
-- 作者:admin -- 发布时间:5/16/2008 6:53:00 PM -- 《RESTful Web Services 中文版》连载 —— 《第一章 Programmable Web的分类》续 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ HTTP:信封里的文档 假如要我对海洋动物进行分类的话,我会先从一些共性(比如DNA、分子结构、胚胎发育规律,等等)开始讨论,然后说明为什么在这些共性上的分化会导致动物之间的差别。为了对programmable web进行分类,我想先对HTTP(所有Web服务共有的协议)作一个概述。 主页时,Web浏览器发出HTTP请求(HTTP request)。我截断了其中超长的两行,以便适应印刷页面的大小。 示例1-5:一个发给[URL=http://www.oreilly.com/index.html]http://www.oreilly.com/index.html[/URL]的HTTP GET请求 GET /index.html HTTP/1.1 也许你对HTTP还不熟悉,所以我来讲解一下上述HTTP请求的各个主要部分。全书都采用这里的术语。 HTTP方法(HTTP method) 对于这个请求,方法是“GET”。在其他讨论“REST”的地方,你会看到HTTP方法也被称作“HTTP动词(HTTP verb)”或“HTTP动作(HTTP action)”。 HTTP方法的名称,就如同编程语言里的方法名称,表示客户端希望服务器如何处理该信封。这里,客户端(即我的Web浏览器)希望从服务器([URL=http://www.oreilly.com/]www.oreilly.com[/URL])获取(GET)一些信息。 路径(path) 这是URI里主机名(hostname)后面的部分。对于这个请求,URI是[URL=http://www.oreilly/]http://www.oreilly[/URL]. com/index.html,路径是“/index.html”。拿信封作比喻的话,路径就是信封上的地址。在本书中,有时“URI”只表示路径。 请求报头(request headers) 它们是一组关键字-值对(key-value pairs),起元数据(metadata)的作用,就像贴在信封上、写有信息的不干胶标贴一样。这个请求有8个报头(headers):Host、User-Agent、Accept,等等。关于HTTP报头有一个标准(参见附录C),不过应用程序也可以定义自己的报头。 这是放在信封里的文档。本例是一个没有实体主体的HTTP请求——也就是说,信封里面是空的!一般来说,GET请求都没有实体主体——完成请求所需的全部信息都在路径(path)和报头(headers)里。 HTTP响应(HTTP response)也是一个放在信封里的文档。HTTP响应跟HTTP请求在形式上差不多。在浏览器向位于oreilly.com的服务器发出如示例1- 5所示的请求后,该服务器将向Web浏览器作出响应。示例1-6显示的是经简化的HTTP响应。 示例1-6:对发给[URL=http://www.oreilly.com/index.html]http://www.oreilly.com/index.html[/URL]的HTTP GET请求的响应 HTTP/1.1 200 OK HTTP响应可分为三个部分。 HTTP响应代码(HTTP response code) 响应报头(response headers) 跟请求报头(request headers)一样,响应报头也是贴在信封上的“不干胶标贴”。本例的HTTP响应有11个报头:Date、Server,等等。 同样地,它也是放在信封里的文档。不过这次它不是空的!该实体主体实现(fulfill)了我的GET请求。 HTTP响应里其余的部分,就像是粘有不干胶标贴的信封,告诉Web浏览器如何来处理这个文档。 有一些非常重要的标签是值得单独提及的。响应报头Content-Type给出实体主体的媒体类型(media type)。对于本例,媒体类型是text/html——这告诉Web浏览器,它可以把实体主体作为一个HTML文档(也就是一个网页)来呈现。 关于媒体类型有一个标准([URL=http://www.iana.org/assignments/media-types/]http://www.iana.org/assignments/media-types/[/URL])。最常见的媒体类型有文本文档(text/html)、结构化数据文档(application/xml)及图像(image/jpeg)等。在别处讨论 REST或HTTP时,你会看到媒体类型也被称作“MIME类型”、“内容类型(content type)”或“数据类型(data type)”。 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ 方法信息 HTTP是programmable web上的所有服务所共有的。 而Web服务之间差别的形成,是因为它们在两个问题上的做法有所不同。 你只要了解一个Web服务是如何处理这两个问题的,就可以清楚地知道该服务是否跟Web的理念保持一致。 第一个问题是:客户端如何把自己的意图传达给服务器。 服务器怎么知道该请求是要获取数据、删除数据、还是改写数据呢? 服务器为什么要做这个操作,而不是做那个操作呢? 例1-7就是一个把方法信息放在URI路径(path)里的Web服务客户端的例子。该客户端调用的是用于在线照片分享的Flickr Web服务,它将搜索Flickr上的照片。 要运行本程序,你得先申请一个Flickr账户,并申请API key(http://www.flickr.com/services/api/keys/apply/)。 例1-7 搜索Flickr上的照片 # 返回指向一个小尺寸的Flickr照片的URI # 在Flickr上搜索与给定tag相匹配的所有照片,并打印出照片的URI。 # 发出HTTP请求,获取实体主体 # 把实体主体作为XML文档来解析 # 对于每个找到的照片… # 主程序 api_key, tag = ARGV 寻找所有photo标签 photo 用REXML::XPath.each(doc, '//photo') 这行简单的Ruby代码可以实现遍历每一个photo标签(tag),而不必遍历整个XML树。 不过这一假设不是总能成立。因为Flickr API除了“获取(GET)”型方法(比如flickr.photos.search和flickr.people.findByEmail)以外,还支持很多其他方法(比如flickr.photos.addTags和flickr.photos.comments.deleteComment等等),而所有这些方法,无论它们是否真的“获取(GET)”数据,都是通过GET请求来调用的。 可见,Flickr把真正的方法信息放在查询变量(query variable)method里的,并希望客户端忽略HTTP方法(HTTP method)。 相比之下,SOAP服务一般是把方法信息放在实体主体(entity-body)和HTTP报头(HTTP header)里的。 例1-8是一段Ruby脚本,它用Google SOAP API来搜索Web。 例1-8 用Google的搜索服务来搜索Web # 做一次Google搜索,并把各个搜索结果打印出来 # 主程序 license_key, query = ARGV 正当我在写这本书时,Google宣布弃用SOAP搜索服务,转而建议采用一种REST式、面向资源的服务(不过,它在使用上要受到条款限制,而SOAP版服务没有这一限制)。 因为Google的SOAP服务是我所知道的最好的例子,而且我也相信你不会实际去运行这个程序,所以我就没修改这个例子了。 你只要看一下这段代码及相关的SOAP和WSDL文档就好了。 嗯,也许从这段代码中看不出什么,因为WSDL库(WSDL library)已经隐蔽了大部分细节。 实际的细节是这样的:当你调用doGoogleSearch方法时,WSDL库会向位于http://api.google.com/search/beta2的Google SOAP服务“端点(endpoint)”发出一个POST请求。所有对Google SOAP API的调用都采用这个URI,而且HTTP方法(HTTP method)总是POST。 例1-9 Google搜索服务的WSDL文档片断 例1-10 一个作SOAP RPC调用的HTTP请求 <?xml version="1.0" encoding="UTF-8"?> 这里的方法信息是“doGoogleSearch”。 我们注意到,上述SOAP信封(Envelope)里有一个XML标签(tag)的名称是 doGoogleSearch,它跟例1-9所示WSDL文档里的那个操作(operation)的名称相同,跟例1-8所示Ruby脚本里的一个方法名称也相同,而且跟HTTP请求报头里的SOAPAction值也相同(有些SOAP实现,会在HTTP请求报头、而不是实体主体里寻找方法信息)。 看过Google SOAP API的例子之后,让我们言归正传,现在来看一下Google搜索引擎本身。 要用浏览器在Google上搜索“REST”,你要向http://www.google.com/search?q=REST发送GET请求,然后你会得到一个HTML响应。 这里,方法信息是放在HTTP方法里的:你在获取(GET)搜索结果列表。 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ 作用域信息 第二个问题是:客户端如何告诉服务器对哪些数据进行操作。 假如服务器已经知道客户端要删除数据了,但它怎么知道客户端要删除的是哪些数据呢? 为什么服务器要对这些数据、而不是对那些数据进行操作呢? 采用什么样的服务设计,决定了哪些信息属于方法信息,哪些信息属于作用域信息。 Flickr和Google就是典型的例子,它们的网站与Web服务功能相同,但却具有不相同的设计。 以Flickr为例,下面两个URI包含同样的信息: 在前一个URI里,方法信息是“获取(GET)”,作用域信息是“具有‘penguin’标签的照片”;而在后一个URI里,方法信息是“搜索照片”,作用域信息是“penguin”。 从技术上看,二者并无区别——它们的HTTP方法都是GET;但只要你回顾一下,并注意到 flickr.photos.search 这个方法名称令HTTP的GET方法失去了原本用意,你就能体会到二者在架构上的区别了。 再看Google的例子: 对于Google SOAP API,方法信息是“做一次搜索(doGoogleSearch)”, 作用域信息是搜索请求(q);而对于Google网站,“search”与“q”的值都属于作用域信息,方法信息是标准的HTTP GET。 (假设Google SOAP API提供了一个名为 doGoogleSearchForREST 的方法的话,那么,由于方法信息里已包含足够信息,因此就不再需要“搜索‘REST’”这样的作用域信息了。) 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/
[此贴子已经被作者于2008-5-16 19:37:31编辑过]
|
-- 作者:admin -- 发布时间:5/16/2008 7:41:00 PM -- 《RESTful Web Services 中文版》连载 —— 《第一章 Programmable Web的分类》续 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ 相互竞争的服务架构 我们已经给出了“不同Web服务会有不同做法”的两个主要问题,现在我要据此对不同风格的Web服务进行分类了。 根据我的研究,常见的Web服务架构主要有三种: REST式架构、RPC式架构和REST-RPC混合架构。 我将依次对它们进行介绍。 REST式、面向资源的架构 REST式架构意味着,方法信息(method information)都在HTTP方法(HTTP method)里; 面向资源的架构(ROA)意味着,作用域信息(scoping information)都在URI里——二者结合起来是很强大的。 一个面向资源的REST式Web服务,通过HTTP请求的第一行(如“GET /reports/open-bugs HTTP/1.1”)就能基本了解客户端要做什么了,HTTP请求的其余部分只是具体细节而已。实际上,很多HTTP请求只要第一行就行了。 如果HTTP方法跟方法信息对不上,那么服务就算不上是REST式的;如果作用域信息不放在URI里,那么服务就不是面向资源的。 虽然并非只有这两条要求,但它们是很好的经验。 • 提供Atom发布协议(http://www.ietf.org/html.char ters/atompub-charter.html)及其变型的服务,例如Gdata(http://code.google.com/apis/gdata/)。 ‡ 令你意想不到的是:用作Web搜索的Google SOAP API,从技术上说,也是一种REST式架构;许多其它只读的SOAP和XML-RPC服务,也是REST式架构。不过,它们这种架构方式并不好,因为它们根本不具备Web的特征。 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ RPC式架构 RPC式Web服务(RPC-style Web Service)通常从客户端收到一个充满数据的信封(envelope),然后发回一个同样充满数据的信封。 RPC式架构意味着:方法信息和作用域信息都在信封(envelope)或报头(headers)里。 具体采用哪种信封,并不影响这里的分类,不过HTTP是一种常见信封格式(毕竟,采用HTTP才称得上是Web服务)。 另一种常见的信封格式是SOAP(把SOAP信封放在HTTP信封里,在HTTP上传送SOAP文档)。 各个RPC式服务采用自己的词汇,就像计算机程序一样 (你每次写程序,定义的函数名称都不相同)。 而REST式Web服务则相反,它们共用一套标准词汇,即HTTP方法。 REST式服务里的每个对象都具有统一的基本接口。 XML-RPC是最典型的RPC架构的例子。 虽然目前XML-RPC主要是一种遗留协议(legacy protocol)了,但由于它相对简单、而且比较容易解释,所以我还是准备从它开始讲起。 例1-11所示的Ruby客户端用于访问一个XML-RPC服务,该服务的作用是查询具有统一产品代码(Universal Product Code)的产品。 例1-11 一个访问XML-RPC服务的例子:根据UPC查询产品 require 'xmlrpc/client' puts find_product("001441000055")['description'] XML-RPC服务就像C语言一样,你可以调用一个带参数(“001441000055”)的函数(lookupUPC),并获得返回值。 例1-12 一个描述XML-RPC请求的XML文档 这个XML文档是放在信封里传给服务器的。这里的信封(envelope)就是一个HTTP请求,它由HTTP方法、URI、报头和实体主体等部分组成,其中实体主体就是上面的XML文档(如例1-13所示)。 例1-13 一个包含了描述XML-RPC请求的XML文档的HTTP信封 <?xml version="1.0" ?> REST式服务为不同的作用域信息暴露不同的URI;而RPC式服务一般为每个“文档处理器”(用于打开信封,并把信封转换为软件指令)暴露一个URI。 我们来做个对比,假设上述UPC查询服务被设计为一种REST式架构的话,那么其客户端代码将如例1-14所示: 例1-14 假想的示例代码: 一个REST式UPC查询服务 较多采用或只采用HTTP POST的服务,多半是RPC式服务。 虽然这不是绝对的,但这至少可以说明该服务没有把HTTP方法用于表达方法信息。 如果一个REST式服务过多地采用HTTP POST,那么它就容易演变为REST-RPC混合架构。 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ REST-RPC混合架构 这一术语是我创造的,它用于形容那些介于REST式架构与纯RPC式架构之间的Web服务。 这些服务通常是由那些对真实Web应用懂得比较多、但对REST理论不精的程序员们创建的。 例1-15 一个向Flickr Web服务发HTTP请求的例子 这是当客户端调用Flickr Web服务时发出的HTTP请求。这里看上去,貌似方法信息是在HTTP方法里的。 这是一个错觉。当一个RPC式服务采用普通老式HTTP(Plain Old HTTP)作为信封格式、且方法信息和作用域信息刚好都在HTTP请求的URI路径里的话,就会产生这种错觉。假如HTTP方法是GET,并且请求服务的意图也是“获取(GET)”信息的话,就会很难分辨方法信息是在HTTP方法里、还是在URI里了。 所以你会把一个RPC式服务的HTTP请求,看成是REST式Web服务的HTTP请求。 这个HTTP请求里可能含有“method=flickr.photos.search”这样的信息,但这个信息会被误认为是作用域信息(就像“photos/”和“search/”是作用域信息一样)。 这些RPC式服务,不经意间或多或少地带着点REST式Web服务的特征。 它们只是把HTTP作为一种信封格式来用,不过它们使用HTTP信封的方式可能刚好跟REST式服务的做法雷同。 许多只读的Web服务,尽管它们起初也许是按RPC风格设计的,但它们都可称得上是完全REST式和面向资源的! 但是,如果该服务允许客户端修改数据的话,就会出现客户端所使用的HTTP方法与真正的方法信息不一致的情况——这样它就不具备REST式服务的特征了。 像这样的服务,我称之为REST-RPC混合服务。 举个例子。即使客户端的意图是修改数据,Flickr Web API仍旧让客户端使用HTTP GET。如果你要删除一个照片,你需要向一个包含“method=flickr.photos.delete”的URI发出GET请求,尽管你的这个请求的意图并不是获取(GET)数据。 Flickr Web API是一种REST-RPC混合架构: 当客户端通过GET方法获取数据时,它是REST式的;当客户端通过GET方法修改数据时,它是RPC式的。 从设计的角度来看,我认为不会有人特意把服务设计为REST-RPC混合架构。 由于HTTP工作方式的原因,任何采用普通HTTP、并暴露多个URI的RPC式服务,往往最终成为REST式架构或者混合架构。 许多程序员按他们设计Web应用的方式来设计Web服务,并最终形成混合架构的服务。 我把“其它架构”称为REST-RPC混合架构,这是众多新词中的一个, 我认为这个新词是看待这些常见而令人困惑的服务的最准确有效的方式。 假如你知道它们的其他称呼(在写本书的时候,“HTTP+POX”是最流行的),你不妨继续往下读,后面我会用我自己的语言来解释这些其它术语。 Human Web是基于Programmable Web的 我写本书的目的,并不是要扩大programmable web的范围——那几乎是不可能的,因为差不多所有具备HTTP接口的事物都已包括在programmable web里了。 我的目的是促进把programmable web建设得更好: 更加统一,更加结构化,并且充分利用HTTP自身的特性。 Programmable Web涉及的技术 HTTP URI XML-RPC 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ SOAP 我下面这则观点可能会引起争议。 基本上,现在每个采用SOAP的Web服务都属于RPC式架构。 因为许多SOAP程序员觉得RPC式架构地位较低,所以更乐意称他们的服务为“面向消息的(message-oriented)”或“面向文档的(document-oriented)”服务,于是他们对我这个观点持有异议。 我要强调的是,这些并不是针对SOAP本身、而是针对SOAP目前的使用状况而言的。 SOAP跟HTTP一样,只是一种把数据放在信封里的方式。 但是现在,唯一放在信封里的数据是像XML-RPC这样关于“如何调用一个远程函数,或者该函数的返回值是什么”的数据。 我会在第十章对此做深入讨论。 WS-* WADL 由于REST式服务的接口比较简单,所以WADL之于REST式服务的必要性,并不像WSDL之于RPC式SOAP服务那么大。 在本书编写之时,已经有少数真实Web服务提供官方的WADL文档了。比如Yahoo!的Web搜索服务就是一例——这是件好事。 其他术语 还有一些在REST讨论中常见术语我没有介绍。 因为我觉得这些术语或者不够准确、或者超出了本书范围,所以我没有提及它们。 不过我还是应该告诉你我为什么这么认为,以便你自己判断。 如果你从没听说过这些词,可以放心跳过本节。
关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ |
-- 作者:admin -- 发布时间:5/16/2008 7:53:00 PM -- 《RESTful Web Services 中文版》连载 ——《第四章 面向资源的架构》 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ 第四章 面向资源的架构 我已经向你展示了REST的能力,但是我还没有系统地讲述该能力是怎样形成的,以及如何来展现该能力。 在这一章,我将概述一个具体的REST式架构——面向资源的架构(Resource-Oriented Architecture,ROA)。 ROA是一种把实际问题转换成REST式Web服务的方法:它令URI、HTTP和XML具有跟其他Web应用一样的工作方式,令程序员们易于使用它们。 • 作用域信息(scoping information)(“服务器发送的为什么是这些数据,而不是其他数据”)——该信息是放在URI里的。这是可寻址性(addressability)原则。 面向资源的架构? 为什么要发明一个新词“面向资源的架构(Resource-Oriented Architecture,ROA)”呢? 为什么不直接用REST? 嗯。我确实在本书的封面上提到REST,并且我认为符合面向资源的架构也符合REST风格。 作为一组设计原则,REST是非常通用的。 具体地说,它并不限定于Web。 REST不依赖于HTTP机制或URI结构。 但因为我讨论的是Web服务,所以我特地用Web相关技术来讲解面向资源的架构(ROA)。我想在特定的编程语言中探讨如何用HTTP和URI来实现REST。 假如将来出现非基于Web的REST式架构,它的最佳实践(best practices)将跟ROA的差不多,只是具体细节会有点差别。 到时我们会有办法解决的。 REST的传统定义留下了一些空白,这给实践者们创造了传播坊间传言(folklore)的环境。 我特意在Roy Fielding的博士论文及W3C相关标准的基础上更进一步——我希望对部分问题做一个了结,把那些坊间传言提炼为一组明确定义的最佳实践。 即便REST是一种架构,用REST来称呼我的架构也是不公平的。 我会把自己的实践经验与建议,与那些关于Web的总体想法结合起来。 我提出ROA这个新词的根本原因是:“REST”这个词被太多地用在派别之争里。 假如某人提到这个词,那通常暗示着,他本人所赞同的架构才是真正的REST式架构,而赞同别的REST式架构的人则会对此持有异议。 尽管REST社区在一些基本问题(比如URI和HTTP的价值)上已基本达成一致,但REST社区还是存在着不同派别。 “面向资源的(resource-oriented)”和“面向资源的架构(resource-oriented architecture)”这样的措辞已经被用于描述一般的REST式架构了。* 我承认“面向资源的架构”并不完全是我原创的词,但我的用法跟先前的用法刚好吻合,而且我觉得采用这个词比声称代表整个REST要好。 什么是资源? 任何事物,只要具有被引用的必要,它就是一个资源(resource)。 如果你的用户“想为它创建一个超文本链接,关于它作一些断言,获取或缓存它的表示,在其他表示里包含它的一部分,标注它,等等”,那么你应该将它作为一个资源。† URIs 是什么,令资源称得上是一个资源? 它必须至少有一个URI。 URI既是资源的名称,也是资源的地址。 如果一则信息没有URI,它就不能算是一个资源,也不能算真正在Web上,而只能算作描述另一个资源的一些数据。 * 我所知道的最早使用“面向资源”一词的例子,是2004年James Snell在IBM developerWorks上发表的一篇文章:“Resource-oriented vs. activity-oriented Web services”(http://www-128.ibm.com/developerworks/xml/ library/ws-restvsoap/)。2006年8月(本书发布之前),Alex Bunardzic使用了“面向资源的架构(Resource-Oriented Architecture)”这个词(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请求为例: 客户端请求 服务器响应 客户端请求 服务器响应 URI是Web的基础技术。 超文本系统在HTML之前就有了,同样地,Internet协议在HTTP之前也已经存在了。不过此前,它们两两之间是没有联系的。是URI把所有这些Internet协议互联起来,形成Web,就如同TCP/IP把各种网络(Usenet、Bitnet、CompuServe等)互联起来,形成Internet一样。 然后,Web征用了那些协议,并消灭了它们,这跟Internet对私有网络所做的一样。 URI应具有描述性 这是ROA在Roy Fielding的博士论文及W3C推荐标准(recommendations)[ ]的为数不多的建议之上提出的第一点。 我建议,资源及其URI应该给人以直觉上的关联。 对于前面我给出的资源,下面是一些不错的URIs: URI应具有一定的结构。 虽然各个URI有所不同,但它们应该有一定的模式:假如/search/Jellyfish是用于获取有关水母的信息的URI,那么用于获取有关老鼠的信息的URI就应该是/search/Mice,而不是/i-want-to-know-about/Mice。 假如客户端知道一个服务的URI的结构,那么它就可以通过构造URI来访问该服务——客户端在使用你的服务方面,将被赋予极大的自由,甚至可能创造出超出想象的使用方式。 URI跟资源的关系 我们来设想一些极端的例子:两个资源有可能是同一个吗? 两个URIs可以指示同一个资源吗? 一个URI可以指示两个资源吗? 根据定义,任何两个资源都不可能是同一个。 假如你声称有两个资源是同一个,那么实际上你所说的只是一个资源。 不过,两个不同的资源在某一时期指向同样的数据,这是有可能的。 假设当前软件版本为1.0.3,那么http://www.example.com/software/releases/1.0.3.tar.gz和http://www.example.com/software/releases/latest.tar.gz将在一定时期内指向同一个文件。 但那两个URIs的意义不同:一个总是指向某一特定版本;另一个总是指向当前时刻的最新版本。 这是两个概念,两个资源。 假设你要报告一个1.0.3版的bug,你不应该使用指向最新版的链接。 一个资源可以有一个或多个URI。 位于http://www.example.com/sales/2004/Q4的销量信息也许另外还有一个URI http://www.example.com/ sales/Q42004。 一个资源有多个URIs的好处是,客户端对该资源的引用变得更容易;坏处是,同一资源具有多个URI将产生“稀释效应”:有的客户端用这个URI,有的客户端用那个URI,而且无法自动验证这些URIs是指向同一个资源的。 一种方案是:假如资源有多个URIs,那么选择其中一个作为该资源的“规范”URI。 当客户端请求该规范URI时,服务器返回响应代码200(“OK”),并附上正确的数据;当客户端请求其他URI时,服务器返回响应代码303(“See Also”),并给出该资源的规范URI。 虽然客户端无法仅凭外观得知这两个URIs是否指向同一资源,但它可以分别向这两个URI发出一个HEAD请求,看是否其中一个URI重定向到另一个,或者二者都重定向到第三个URI。 还有一个方案,就是对所有这些URI一视同仁、作同样的响应;但是对于非规范URI的请求,将在响应报头Content-Location里给出“规范”URI。 因为 sales/2004/Q4 和 sales/Q42004 是同一个资源“2004年第4季度销售信息”的不同URIs,所以你将从这两个URIs获得相同的字节流。 虽然 releases/1.0.3.tar.gz 和 releases/latest.tar.gz 是不同的资源(前者表示“1.0.3版”,后者表示“最新版”),但它们也可能会返回相同的字节流。 可寻址性 我已经对资源及资源的URI作了介绍,现在我可以进一步探讨ROA的两个特性了:可寻址性(addressability)和无状态性(statelessness)。 如果一个应用将其数据集里有价值的部分作为资源(resources)发布出来,那么该应用就是可寻址的(addressable)。 因为资源是通过URI暴露的,所以一个可寻址的应用会为它可能提供的每一则信息都发布一个URI。 一般来说,URI的数量是无限的。 这不是学术上的担忧。 在90年代中期 ftp:// URI流行起来之前,那时人们只能这样写:“用FTP匿名登陆ftp.example.com,然后进入 pub/files/ 目录,再下载文件 file.txt。”URI使得FTP具有跟HTTP一样的可寻址性。 现在人们只要写“下载ftp://ftp.example.com/pub/files/file.txt”就行了。 虽然在技术上步骤还是一样的,但现在这些步骤可以由机器自动完成了。 因为HTTP和Google都是可寻址的,所以我可以在书中给出该URI。 你可以在浏览器地址栏里输入这个URI,以直接进入Google搜索应用的特定状态。 页面打开之后,你可以把该页面加入书签,以便以后使用。 你可以在自己的网页里提供到该URI的链接。 你还可以通过Email把该URI告诉别人——假如HTTP不是可寻址的话,那你只有把整个页面下载下来,然后将该HTML文件作为附件发给其他人。 为了节省带宽,你可以为你的本地网络设置一个HTTP代理缓存(proxy cache)。 当第一次有人请求 http://www.google.com/search?q=jellyfish 时,该缓存将在本地保存一份该文档的副本;当下次有人再访问同样的URI时,该缓存就直接把该副本发给请求者,而不是重新下载一次。 这一切得以实现,要求每个页面都有一个唯一的标识串,即一个地址(address)。 在Amazon S3里,每个桶(bucket)、每个对象(object)都有自己的URI,所以S3服务是可寻址的。 尚未创建的桶或对象虽然还不是资源,但它们也有自己的URI,你可以通过向它的URI发出PUT请求来创建该资源。 电子表格里的单元格也是可寻址的,你可以在公式里引用单元格,这样公式在求值时就会用到被引用的单元格的值。 URI就好比这里的文件路径和单元格。 Web应该是可寻址的,这似乎是理所当然的。可是,许多Web应用却不是可寻址的,尤其是Ajax应用。 正如我将在第十一章说到的,大多数Ajax应用只是调用REST式或混合Web服务的客户端,但当你把它当作网站来使用时,你会发现它跟网站的感觉不太像。 我就不在这里反复批评他们了。让我们继续关于Google特性的讨论,我们现在来看Gmail在线Email服务。 在最终用户看来,Gmail的URI始终是https://mail.google.com/。无论你作什么操作,无论你从Gmail获取什么信息或者向Gmail上传什么信息,你不会看到别的URI。 “关于水母的电子邮件讯息”这一资源不是可寻址的,但前面提到的“有关水母的资源列表”这个资源是可寻址的。‡ 不过,正如我将在第十一章所展示的,在Gmail应用背后存在一个可寻址的网站——在那里,“有关水母的电子邮件讯息”这个资源确实有一个URI,即 https://mail.google.com/mail/?q=jellyfish&search=query&view=tl。问题是,该网站不是你直接使用的;该网站是一个真实的Web服务,其真正调用者是一个在你的浏览器里运行的JavaScript程序。§ Gmail Web服务是可寻址的,不过调用该服务的Gmail Web应用不是可寻址的。 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ 无状态性 可寻址性是ROA的四个主要特征之一。 ROA的第二个特征是无状态性(statelessness)。 我会给你两个无状态性的定义:一个较为一般性的定义,和一个更切合ROA实际的定义。 ‡ Gmail有一个可寻址的版本,位于URI https://mail.google.com/mail/?ui=html。如果你用这个纯HTML接口的话,那么“关于水母的电子邮件讯息”这个资源就是可寻址的了。 你在请求有关老鼠或有关水母的资源列表时,你无法一次获得完整的列表。 你会先得到列表中的一页,里面包括10条左右搜索引擎认为与你的查询最为匹配的结果。 如果你想获得更多结果,你需要再做一次HTTP请求。 第二页及其后各页都是该应用的不同状态,所以它们需要有自己的URI(比如http://www.google.com/search?q=jellyfish&start=10)。如同所有可寻址的资源一样,该应用状态也可被发给其他人、被缓存或者被加入书签(以便以后回到该状态)。 图4-1是一个简单的状态图,它显示了一个HTTP客户端是如何与一个搜索引擎的四个状态进行交互的。 因为客户端每次请求后都回到最初的状态,所以这是一个无状态的应用。 各个请求是相互独立的。 客户端可以按任意次序向这些资源发出任意次请求。 它可以在请求第2页之前请求第1页(或者根本不请求第1页),服务器对此不会介意。 作为对比,图4-2给出了假设上述搜索引擎是一个有状态的应用的情形:一个状态推进到另一个状态,就像许多桌面应用那样。 倘若HTTP被设计为允许有状态的交互,那么HTTP请求将简单许多:当客户端启动一个搜索引擎会话时,搜索引擎可以自动启动搜索,而不必客户端发送搜索请求,因为搜索引擎保存有该客户端上次搜索的记录;假如客户端正在查看前10条结果,接着要看第11至20条结果,那么只要发送一个“start=10”的请求就行了,而不必发送 /search?q=mice&start=10,重复之前的请求“我要搜索,具体地说要搜索老鼠。” 状态可以简化单个HTTP请求,但它会令整个HTTP协议变得更加复杂。 FTP客户端要比HTTP客户端复杂许多,原因就在于客户端和服务器的会话状态必须要保持同步。 即使在可靠的网络上,这也是一项复杂的工作,何况Internet还不是一个可靠的网络。 从协议里去掉状态,将大大减少出错条件。 因为每次交互都只包括单个请求,所以服务器不必为客户端超时而担忧。 无状态性还引入了一些新特性。 在负载均衡(load-balanced)服务器上分配无状态的应用将容易许多:因为各个请求之间没有相互依赖,所以它们可被放在不同的服务器上处理,而不需服务器之间作任何协作;要提升规模,只需往负载均衡系统里添置更多服务器就行了。 对无状态的应用作缓存也是比较容易的:只要看一下请求,就可以决定是否要缓存一个HTTP请求的结果了;前一个请求的状态不会影响对当前请求的缓存处理。 但那些URI得包含真正的状态才行,而不是仅包含一个跟“保存在服务器上的状态信息”对应的键(key)。start=10自身具有一定含义,而PHPSESSID=27314962133则不。REST式架构要求把状态保存在客户端,并且在发给服务器的每个请求中都包括这些状态(假如需要的话)。 关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/ 应用状态 VS 资源状态 我们在谈论“无状态性”时,把什么视为“状态”呢? 我们调用Web服务,是因为服务可以提供我们所需要的数据,那么这个服务端的持久化数据(persistent data)跟这个我们极力避免保存在服务端的状态(state)有何区别呢? Flickr Web服务允许你往自己的帐户上传图片,那些图片是保存在服务器上的。 但是,假如仅仅出于不让服务器保存任何状态的目的,而要求客户端在发给flickr.com的每个请求里都附上所有图片,那就太麻烦了。 那将毁灭该服务的整个目标。 但这一场景,跟前面我提到的那个我主张不应放在服务器上的客户端会话状态,有何不同呢? 这是一个术语的问题。 无状态性(statelessness)意味着:有一种状态,是服务器不应保存的。 实际上,状态分两种。 从现在开始,我将区分应用状态(application state)与资源状态(resource state):前者应该保存在客户端,后者应该保存在服务端。 当你使用搜索引擎时,你的搜索请求和当前页码就属于应用状态。这些状态是因客户端而异的,可能你在浏览“水母”搜索结果的第3页,而我在浏览“老鼠”搜索结果的第1页。 由于我们使用搜索引擎的轨迹不同,所以我们的搜索请求与当前页码不一样。 我们各自的客户端分别保存着各自的应用状态(application state)。 一个Web服务,仅当实际收到你的请求时才关心你的应用状态,其他时刻,你的存在对它没有意义。 这就是说,每当客户端向服务器发请求时,请求里必须包含服务器处理该请求所需的所有应用状态。服务器会向客户端返回一些含有链接(这些链接代表客户端可以继续发出的请求)的页面,然后服务器与客户端的本次交互就结束了。当客户端向服务器发出下一个请求时,将建立另一次交互——我说Web服务应该是“无状态的(stateless)”,指的就是这个意思。 各个客户端应当自己管理自己的应用状态。 资源状态(resource state)对于每个客户端都是相同的,它应当保存在服务端。 当你向Flickr上传一个图片时,你就为它创建了一个新资源:这个新建的图片拥有自己的URI,你可以向这个URI发出请求。 你可以通过HTTP来获取、修改或删除这个“图片”资源。 它是所有人的资源,我也可以获取它。 该图片是一个资源状态,它一直保存在服务器上(直到被删除)。 应用状态有时会被用于非你所期望的场合。 许多Web服务会让你注册一个称为“API key”或“应用key”的唯一字符串,并要求你给每个请求都附上这个key,这样,服务器就可以通过这个key来限定你一天最多发送多少次请求。 例如,Google SOAP搜索服务(Google已弃用该服务)的API key,一天可以用于1000个请求。 一个key一天发出请求的数量就是客户端的状态,每个客户端都不一样。 一旦你的请求数量超出限制,服务对请求的处理将发生显著变化:你的第1000个请求可以成功获取所需的数据,但你的第1001个请求将得到错误信息。
|
-- 作者:hjx_221 -- 发布时间:7/4/2008 10:02:00 PM -- 慢慢看看 谢谢了 |
-- 作者:hanling911 -- 发布时间:7/7/2008 2:42:00 PM -- 很好,很强大 |
-- 作者:supercreateboy -- 发布时间:7/26/2008 9:36:00 AM -- 感谢分享 |
-- 作者:jacken -- 发布时间:8/23/2008 7:47:00 PM -- 好书 不错 谢谢!! |
-- 作者:dicolor -- 发布时间:9/16/2008 6:01:00 PM -- 好长的文章,慢慢看了 |
-- 作者:naonao -- 发布时间:10/10/2008 11:29:00 AM -- 很好 |
-- 作者:zhxiaomiao -- 发布时间:10/14/2008 5:16:00 PM -- 没事了 就来看看 |
W 3 C h i n a ( since 2003 ) 旗 下 站 点 苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》 |
1,148.438ms |