新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     >>W3CHINA.ORG讨论区<<     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> Web服务(Web Services,WS), 语义Web服务(Semantic Web Services, SWS)讨论区: WSDL, SOAP, UDDI, DAML-S, OWL-S, SWSF, SWSL, WSMO, WSML,BPEL, BPEL4WS, WSFL, WS-*,REST, PSL, Pi-calculus(Pi演算), Petri-net,WSRF,
    [返回] W3CHINA.ORG讨论区 - 语义网·描述逻辑·本体·RDF·OWLW3CHINA.ORG讨论区 - Web新技术讨论『 Web Services & Semantic Web Services 』 → 《RESTful Web Services 中文版》连载 —— 《前言》 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 125292 个阅读者浏览上一篇主题  刷新本主题   平板显示贴子 浏览下一篇主题
     * 贴子主题: 《RESTful Web Services 中文版》连载 —— 《前言》 举报  打印  推荐  IE收藏夹 
       本主题类别: SOA/EAI基础    
     admin 帅哥哟,离线,有人找我吗?
      
      
      
      威望:9
      头衔:W3China站长
      等级:计算机硕士学位(管理员)
      文章:5255
      积分:18406
      门派:W3CHINA.ORG
      注册:2003/10/5

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给admin发送一个短消息 把admin加入好友 查看admin的个人资料 搜索admin在『 Web Services & Semantic Web Services 』的所有贴子 点击这里发送电邮给admin  访问admin的主页 引用回复这个贴子 回复这个贴子 查看admin的博客楼主
    发贴心情 《RESTful Web Services 中文版》连载 ——《第四章 面向资源的架构》


    关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/

    第四章 面向资源的架构

    我已经向你展示了REST的能力,但是我还没有系统地讲述该能力是怎样形成的,以及如何来展现该能力。 在这一章,我将概述一个具体的REST式架构——面向资源的架构(Resource-Oriented Architecture,ROA)。 ROA是一种把实际问题转换成REST式Web服务的方法:它令URI、HTTP和XML具有跟其他Web应用一样的工作方式,令程序员们易于使用它们。
    在第一章,我根据REST式Web服务(RESTful web services)在两个问题上的不同做法来对它们进行分类。 这两个问题分别对应于REST的四个标志特征中的两个:

    •  作用域信息(scoping information)(“服务器发送的为什么是这些数据,而不是其他数据”)——该信息是放在URI里的。这是可寻址性(addressability)原则。
    •  方法信息(method information)(“为什么服务器应当发送而不是删除这些数据?”)——该信息是放在HTTP方法里的。 HTTP方法只有少数几种,而且人人都事先知道这些方法的作用。这是统一接口(uniform interface)原则。
    在这一章,我将介绍面向资源的架构(ROA)的功能成分:资源、资源名称、资源的表示、资源间的链接。 我将解释并宣传ROA的特性:可寻址性(addressability)、无状态性(statelessness)、连通性(connectedness)和统一接口(uniform interface)。 我将展示Web技术(HTTP、URI和XML)是如何实现这些功能成分、以获得上述特性的。
    在前面几章,我通过现有的一些Web服务(如S3)举例说明了一些概念。 在本章,我除了会引用一些现有的Web服务(web services)以外,还会引用一些现有的网站(web sites)来讲解概念。 希望此刻我已经让你相信“网站就是Web服务,而且许多Web应用(比如搜索引擎)就是REST式Web服务”了。 我在讲述一些抽象概念(如可寻址性)时,会给出一些真实的URIs,这样你就可以通过在浏览器里输入这些URIs来亲身体会有关概念了。

    面向资源的架构?

    为什么要发明一个新词“面向资源的架构(Resource-Oriented Architecture,ROA)”呢? 为什么不直接用REST? 嗯。我确实在本书的封面上提到REST,并且我认为符合面向资源的架构也符合REST风格。  

    但是,REST并不是一种架构,而是一组设计原则。 你可以讲“在遵守这些原则方面,一个架构做得比另一个架构好”,但是你不能讲“REST架构”,因为不存在一个叫“REST架构”的东西。
    直到目前为止,人们已经习惯于在设计服务时,根据他们自己对REST的理解发明一次性架构(one-off architectures)。 这样做最显著的结果,是产生了各式各样的REST-RPC混合Web服务,而其创建者们还声称它们为REST式的。 为此,我提出了一组构建真正REST式Web服务的具体规则,以期能够结束这一状况。 在接下来的两章里,我会给出一些简单的步骤,你只要根据这些步骤就能把需求转换成一个个资源(resources)。 即便你不喜欢我的规则,你至少可以知道做哪些改变是不会导致违反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社区还是存在着不同派别。
    最理想的状况是没有派别之争。可是,太多的经验告诉我,仅凭意愿并不能结束它。 所以,我要给我的关于“应当如何设计REST式应用”的思想取一个不同的名字。 这样,万一我的这些想法被不可避免地用到派别之争中,那些不同意我的人就可以针对我的面向资源的架构(ROA),而不是跟REST式架构或一般的REST混为一谈。 先要把概念理清了,才可能做到理解。

    “面向资源的(resource-oriented)”和“面向资源的架构(resource-oriented architecture)”这样的措辞已经被用于描述一般的REST式架构了。* 我承认“面向资源的架构”并不完全是我原创的词,但我的用法跟先前的用法刚好吻合,而且我觉得采用这个词比声称代表整个REST要好。

    关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/

    什么是资源?

    任何事物,只要具有被引用的必要,它就是一个资源(resource)。 如果你的用户“想为它创建一个超文本链接,关于它作一些断言,获取或缓存它的表示,在其他表示里包含它的一部分,标注它,等等”,那么你应该将它作为一个资源。†
    通常,一个资源就是某个可以存放在计算机上并体现为比特流的事物,比如:一个文档、数据库里的一条记录、或者运行某算法的结果等等。 一个资源(resource)可以是一样实物(比如一只苹果),也可以是一个抽象的概念(比如勇气)——但正如我们将看到的,这类资源的表示(representation)肯定会令我们大失所望。
    下面是一些资源的例子:
    * 某软件的1.0.3版
    * 某软件的最新版本
    * 2006年10月24日发布的第一篇博客文章
    * 一张阿肯色州小石城的地图
    * 关于水母的一些信息
    * 与水母有关的资源列表
    * 大于1024的最小素数
    * 大于1024的第五小素数
    * 2004年第4季度的销量
    * Alice和Bob两人之间的关系
    * bug数据库里的待解决bug列表

    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请求为例:

    客户端请求            服务器响应
    GET /hello.txt         Hello, world!


    HTTP客户端是这样处理资源的:首先连接到该资源所在服务器(本例中www.example.com),然后向该服务器发送方法(“GET”)及该资源的路径(“/hello.txt”)。 虽然现在的HTTP 1.1比HTTP 0.9稍微复杂一些,但它们的工作原理是一样的。服务器域名及路径都在资源的URI里。

    客户端请求         服务器响应

    GET /hello.txt         HTTP/1.1
                           Host:www.example.com
                           200 OK
                           Content-Type: text/plain
                           Hello, world!

    Tim Berners-Lee在Universal Resource Identifiers—Axioms of Web Architecture (http://www.w3.org/DesignIssues/Axioms)这篇文章里很好地概括了URI背后的原理。我会在本节详细讲解构造URI及为资源分配URI的原理。

    URI是Web的基础技术。 超文本系统在HTML之前就有了,同样地,Internet协议在HTTP之前也已经存在了。不过此前,它们两两之间是没有联系的。是URI把所有这些Internet协议互联起来,形成Web,就如同TCP/IP把各种网络(Usenet、Bitnet、CompuServe等)互联起来,形成Internet一样。 然后,Web征用了那些协议,并消灭了它们,这跟Internet对私有网络所做的一样。
    如今,我们浏览Web(而不是Gopher),我们从Web(而不是FTP站点)上下载文件,我们在Web(而不是WAIS)上搜索出版物,我们在Web(而不是Usenet新闻组)上交谈。 版本控制系统(如Subversion、arch等)也是基于Web的(而不是采用专有的CVS协议)。 甚至连Email也逐渐转向Web了。
    Web之所以能够把其它协议消灭,是因为它采取了一种简单的方式来给可用资源作标签,而这是许多其它协议所没有的。 Web上的每个资源都至少有一个URI。 你可以把URI写在户外广告牌上, 人们可以看到那个广告牌后,可以在Web浏览器里打开那个URI,直接进入你期望他们进入的资源。 这似乎比较不可思议,但在URI被发明之前,这种我们司空见惯的交互是不可能实现的。

    关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/

    URI应具有描述性

    这是ROA在Roy Fielding的博士论文及W3C推荐标准(recommendations)[ ]的为数不多的建议之上提出的第一点。 我建议,资源及其URI应该给人以直觉上的关联。 对于前面我给出的资源,下面是一些不错的URIs:
    * 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应具有一定的结构。 虽然各个URI有所不同,但它们应该有一定的模式:假如/search/Jellyfish是用于获取有关水母的信息的URI,那么用于获取有关老鼠的信息的URI就应该是/search/Mice,而不是/i-want-to-know-about/Mice。 假如客户端知道一个服务的URI的结构,那么它就可以通过构造URI来访问该服务——客户端在使用你的服务方面,将被赋予极大的自由,甚至可能创造出超出想象的使用方式。
    正如我们在下一章的“命名资源”一节中将看到的,这并不是REST的必备规则。 从技术上讲,URI不是非得具备一定结构或模式,但我认为需要这样。 良好的Web设计需要这条规则,REST式及混合架构服务也一样。

    URI跟资源的关系

    我们来设想一些极端的例子:两个资源有可能是同一个吗? 两个URIs可以指示同一个资源吗? 一个URI可以指示两个资源吗?

    根据定义,任何两个资源都不可能是同一个。 假如你声称有两个资源是同一个,那么实际上你所说的只是一个资源。 不过,两个不同的资源在某一时期指向同样的数据,这是有可能的。 假设当前软件版本为1.0.3,那么http://www.example.com/software/releases/1.0.3.tar.gzhttp://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只能指示(designate)一个资源。 假如一个URI指示多个资源的话,它就不是统一资源标识符(Universal Resource Identifier)了。 不过,当你请求一个URI时,服务器可以返回给你关于多个资源的信息:包括你所请求的资源,也包括其它相关资源的信息。 在你获取一个网页时,一般服务器只返回该网页的一些信息,但其中也会包含一些指向其它网页的链接。 在你用Amazon S3客户端获取一个S3桶(bucket)时,你将得到一个文档,其中既包含关于该桶的信息,也包含关于相关资源(即桶里的对象)的信息。

    可寻址性

    我已经对资源及资源的URI作了介绍,现在我可以进一步探讨ROA的两个特性了:可寻址性(addressability)和无状态性(statelessness)。

    如果一个应用将其数据集里有价值的部分作为资源(resources)发布出来,那么该应用就是可寻址的(addressable)。 因为资源是通过URI暴露的,所以一个可寻址的应用会为它可能提供的每一则信息都发布一个URI。 一般来说,URI的数量是无限的。

    从最终用户角度来看,可寻址性(addressability)对任何网站或Web应用来说,都是最重要的方面。 用户很聪明,只要数据有充分的价值,即便有一些缺陷,他们也不会在意、或者会有办法变通;但假如不具备可寻址性,用户就没辙了。
    设想一个指示(designate)“有关水母的资源列表”这个资源的URI:http://www.google.com/search?q=jellyfish。这个在Google里搜索水母(jellyfish)的URI跟 http://www.google.com 一样都是真实的URI。但是,假如HTTP不是可寻址的,或者Google搜索引擎不是可寻址的Web应用,那么我就无法在书里直接给出这个URI了——我只能告诉你:“用浏览器打开google.com,在搜索框里输入‘jellyfish’,然后点击‘Google 搜索’按钮。”

    这不是学术上的担忧。 在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)。
    一个URI可以作为另一个URI的输入。 比如,你可以用一个外部Web服务来验证某个网页的HTML代码是否符合规范,或者把某个网页里的文本翻译为另一种语言。 这些Web服务都以URI作为输入。 假如HTTP不是可寻址的,那你就无法告诉它们你希望对哪个资源进行操作了。

    在Amazon S3里,每个桶(bucket)、每个对象(object)都有自己的URI,所以S3服务是可寻址的。 尚未创建的桶或对象虽然还不是资源,但它们也有自己的URI,你可以通过向它的URI发出PUT请求来创建该资源。
    你的计算机上的文件系统也是一个可寻址的系统,命令行应用可以接受文件路径为参数,然后对该文件进行一些操作。

    电子表格里的单元格也是可寻址的,你可以在公式里引用单元格,这样公式在求值时就会用到被引用的单元格的值。 URI就好比这里的文件路径和单元格。
    可寻址性(addressability)是Web应用最大的优点,它令客户端可以灵活自由地使用网站(甚至可能创造出超出网站设计者想象的使用方式)。 遵照可寻址性规则,将给你和你的用户带来REST的很多优点。 REST-RPC服务如此常见的原因就在于,它们把可寻址性跟过程调用编程模型(procedure-call programming model)结合起来了。 我在“Resource-Oriented Architecutre”中把“resource”写在最前面,是因为具备可寻址性的是资源。

    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实际的定义。
    无状态性(Statelessness)意味着每个HTTP请求都是完全孤立的。 当客户端发出一个HTTP请求时,请求里包含服务器实现(fulfill)该请求所需的全部信息,服务器不依赖任何之前请求提供的信息。 假设本次请求需要之面某个请求提供的信息,那么客户端应当把那个信息也包括在本次请求里。

    ‡ Gmail有一个可寻址的版本,位于URI https://mail.google.com/mail/?ui=html。如果你用这个纯HTML接口的话,那么“关于水母的电子邮件讯息”这个资源就是可寻址的了。
    § Python的libgmail库(http://libgmail.sourceforge.net/)也可以调用这个Web服务。

    更实际地,我们从可寻址性方面来考虑无状态性。 可寻址性要求:服务器所能提供的每一则有价值的信息都应该作为资源来发布,而且每个资源都有自己的URI。无状态性要求:服务器可能的状态也是资源,也应该有自己的URI。 客户端不必为了让某个请求可被服务器接受而诱使服务器进入某状态。
    在human web上,你经常会碰到这样的情况:你的浏览器的“返回(back)”按钮不起作用了,你无法在你的浏览历史里前后或进退了。 有时,这是因为你执行了一个不可撤销的操作(比如发布了一篇博客文章,或购买了一本书),不过经常这是由于你所浏览的网站违反了无状态性原则:该网站期望你按照一定的顺序来发出请求,比如先发A请求,再发B请求,然后再发C请求;假如你在发出B请求后,又发了一个B请求(而不是它所期望的C请求),那么它将无法理解你的行为。
    我们再来看一个搜索的例子。 搜索引擎是一个具有无数状态的Web服务:至少对于你搜索的每个串,它都有一个与之对应的状态。每个状态都有自己的URI。 你可以用URI http://www.google.com/search?q=mice 向该服务请求一个有关老鼠(mice)的资源列表。 你可以用URI http://www.google.com/search?q=jellyfish 向该服务请求一个有关水母(jellyfish)的资源列表。 如果你写不出这个URI,你可以先打开 http://www.google.com/,然后填写表单执行搜索。

    你在请求有关老鼠或有关水母的资源列表时,你无法一次获得完整的列表。 你会先得到列表中的一页,里面包括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,重复之前的请求“我要搜索,具体地说要搜索老鼠。”


    此主题相关图片如下:
    按此在新窗口浏览图片
    图4-1 一个无状态的搜索引擎


    此主题相关图片如下:
    按此在新窗口浏览图片
    图4-2 一个有状态的搜索引擎


    FTP就是这样的。FTP有一个“工作目录(working directory)”的概念,除非你改变它,否则它在整个会话过程中不会变化。 你可以登录一个FTP服务器,用cd命令进入某个目录,然后用get命令下载一个该目录下的文件。如果你要下载同目录下的其他文件,直接用get命令下载就行了,不需要重新用cd命令进入目录。 为何HTTP不支持这样呢?

    状态可以简化单个HTTP请求,但它会令整个HTTP协议变得更加复杂。 FTP客户端要比HTTP客户端复杂许多,原因就在于客户端和服务器的会话状态必须要保持同步。 即使在可靠的网络上,这也是一项复杂的工作,何况Internet还不是一个可靠的网络。

    从协议里去掉状态,将大大减少出错条件。 因为每次交互都只包括单个请求,所以服务器不必为客户端超时而担忧。

    因为客户端发出的每个请求里都含有全部所需信息,所以服务器绝不会搞错客户端所处的状态。 决不会出现“因为服务器保存了某个没有告诉客户端的状态,而导致客户端在错误的‘工作目录’下执行操作”的情况。

    无状态性还引入了一些新特性。 在负载均衡(load-balanced)服务器上分配无状态的应用将容易许多:因为各个请求之间没有相互依赖,所以它们可被放在不同的服务器上处理,而不需服务器之间作任何协作;要提升规模,只需往负载均衡系统里添置更多服务器就行了。 对无状态的应用作缓存也是比较容易的:只要看一下请求,就可以决定是否要缓存一个HTTP请求的结果了;前一个请求的状态不会影响对当前请求的缓存处理。
    客户端也将从无状态性中获益。 客户端可以在浏览到第50页有关“老鼠”的搜索结果时,把当前URI(/search?q=mice&start=500)加入书签——这样,客户端可以在一周后方便地回到这个状态,而不必重新历经许多前面的状态。 在一个HTTP会话里经历几小时后得到的有效URI,可以在新会话里直接访问,而且同样有效。
    要令你的服务具备可寻址性(addressability),你需要花费一些努力、把应用的数据划分为资源的集合。 由于HTTP本身是一个无状态的协议,所以你编写的Web服务默认地具备无状态性,你需要花费一些努力来改变它。
    改变无状态性,最常用方法就是利用HTTP会话(session)。 当一个用户首次访问你的网站时,他会得到一个唯一的字符串,用以标识他在该网站上的会话。 这个字符串可以保存在cookie里,也可以放在给该用户的所有URI里。 这是一个Rails应用设置的会话cookie:
    Set-Cookie: _session_id=c1c934bbe6168dcb904d21a7f5644a2d; path=/
    这是一个PHP应用在URI里给出会话ID的例子: http://www.example.com/forums?PHPSESSID=27314962133
    值得注意是,这些无意义的十六进制或十进制字符串并不是状态。 状态保存在服务器端的某个数据结构里,这些字符串只是该数据结构的键(key)。 有状态的URIs(stateful URIs)并不违反REST精神:服务器通过这种方式把下一个状态告诉客户端。 (但是,cookies是有点违反REST精神的,我会在第八章“Cookies的问题”一节中详细谈到。) cookie会对Web服务客户端产生“如同Web浏览器的‘返回(back)’按钮失灵”一样的影响。
    设想Google搜索引擎返回的一个HTML网页里有一个URI,该URI里包含查询变量 start=10。 也就是说,服务器向客户端发送了下一个可选的状态。

    但那些URI得包含真正的状态才行,而不是仅包含一个跟“保存在服务器上的状态信息”对应的键(key)。start=10自身具有一定含义,而PHPSESSID=27314962133则不。REST式架构要求把状态保存在客户端,并且在发给服务器的每个请求中都包括这些状态(假如需要的话)。
      
    服务器可以通过向客户端发送有状态的URIs(stateful URIs)为客户端提供进入新状态的机会,但服务器自己不应保存任何状态。

    关于本书更多信息,请看本书官方网站: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个请求将得到错误信息。

    与此同时,我才发出第402个请求,所以服务器仍然为我服务。
    显然,服务器不能依靠客户端自己报告“请求计数”这一应用状态(application state),因为客户端有可能故意报错数字,以欺骗服务器。 但假如服务器自己保存该应用状态的话,就会违反无状态性(statelessness)原则。 这里的API key如同前面那个Rails应用里的_session_id cookie,它是用于获取保存在服务器端的客户端会话(有效期为1天)的键(key)。 这在一定程度上没什么问题,不过将导致可伸缩性(scalability)问题:假如该服务被分布于多台机器上,那么集群中的每台机器都得知道“你发出的是第1001个请求,而我发出的是第402个请求”(用行话讲,这叫会话复制(session replication))才行,只有这样才能让每台机器都知道应该“拒绝你的请求,允许我的请求”。 另一种方案是负载均衡系统要确保:对于你的每个请求,每次都让集群里的同一台机器来处理(用行话讲,这叫会话亲缘性(session affinity))。 无状态性将免除这些要求。 所以作为一名服务设计者,只有当你的资源状态需要被划分到不同机器上时,你才需要考虑数据复制(data replication)的问题。


    关于本书更多信息,请看本书官方网站:http://restfulwebservices.cn/

    ----------------------------------------------

    -----------------------------------------------

    第十二章第一节《用ROR创建面向资源的服务》
    第十二章第二节《用Restlet创建面向资源的服务》
    第三章《REST式服务有什么不同》
    InfoQ SOA首席编辑胡键评《RESTful Web Services中文版》
    [InfoQ文章]解答有关REST的十点疑惑

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2008/5/16 19:53:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 Web Services & Semantic Web Services 』的所有贴子 点击这里发送电邮给Google AdSense  访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/5/3 10:34:59

    本主题贴数12,分页: [1] [2]

     *树形目录 (最近20个回帖) 顶端 
    主题:  《RESTful Web Services 中文版》连载 ..(18754字) - admin,2008年5月16日
        回复:  没事了 就来看看(15字) - zhxiaomiao,2008年10月14日
        回复:  很好(4字) - naonao,2008年10月10日
        回复:  好长的文章,慢慢看了(20字) - dicolor,2008年9月16日
        回复:  好书 不错 谢谢!!(16字) - jacken,2008年8月23日
        回复:  感谢分享(8字) - supercreateboy,2008年7月26日
        回复:  很好,很强大(12字) - hanling911,2008年7月7日
        回复:  慢慢看看谢谢了(16字) - hjx_221,2008年7月4日
        回复:  《RESTful Web Services 中文版》连载 ..(25223字) - admin,2008年5月16日
        回复:  《RESTful Web Services 中文版》连载 ..(16451字) - admin,2008年5月16日
        回复:  《RESTful Web Services 中文版》连载 ..(13868字) - admin,2008年5月16日
        回复:  《RESTful Web Services 中文版》连载 ..(8550字) - admin,2008年5月16日

    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    2,660.156ms