在RESTful应用程序中的超媒体

日期: 2008-03-24 作者:Mark Baker翻译:王志雄 来源:TechTarget中国

  超媒体到底是什么?!


  如果你有幸听说过REST架构风格,那么你也可能听说某些人认为统一接口是最重要的约束,尤其是该接口在制约资源上能被调用的方法方面。但是,你没有意识到的是,对于统一接口还有很多东西。特别是一个被笨拙地冠以“超媒体即应用状态引擎(hypermedia as the engine of application state)”名字的子约束,它可以认为是REST最重要的约束,在某种意义上它独力塑造了我们所熟知的RESTful系统的大部分的“形状”。


  定义


  不幸的是,REST论文没有在这个约束上展开,除了它的名字和对它实际应用的描述:


  所以,模型应用是一个从一个状态迁移到下一个状态的引擎,迁移是通过对当前表述性集合中可选状态进行检查和选择而完成的。


  尽管它给我们提供了一个有用的描述,但是在我看来,它不能帮助我们真正理解约束本身的作用域;约束真正允许的是什么,不允许的又是什么。由此开始,接下来还值得注意的是我们能从约束自身名字中获取些什么信息。


  “应用状态”指的是一个状态,它决定了用户在完成一个任务的流程中所处的“位置”。例如,在做个人银行业务的时候,用户是正在浏览帐户余额,还是在填写账目付款单,或者是在订购新的支票?它们每个都是不同的应用状态。有些人错误地认为“状态”是指资源状态,在上面例子里,资源是指帐户余额或者近期付款清单。但“应用状态”和“资源状态”是不同的。


  应用状态也被称为“会话状态”,该状态也是REST的“无状态”约束所指的状态,这种约束要求客户端独自维持状态。反之,如果你使用诸如VNC或者Windows远程桌面的远程会话技术,那么应用状态完全保存在服务器上。


  Ted Nelson在1962年创造了“超媒体”一词,是他发明的“超文本”泛化。鉴于超文本产生了内部互联的文本文档,那么超媒体将范围扩展到了任何形式的媒体。当然,两者的关键点都是我们可以在使用的内容嵌入链接。


  约束实战


  REST在2003/2004年开始获得一些从事面向互联网服务开发者的关注——至少这些开发者确实将他们的服务冠以“REST”的绰号——最明显的是两个高调、自我标榜的“REST API”:Flickr和Amazon。有趣的是两个服务也同时提供了基于SOAP的接口,但是与“REST API”相比,这两个SOAP API都没有显出更多的使用。最终,REST社区拥抱了这些服务,并且将它们作为一种进一步解释在Web上使用REST风格的价值和魅力的工具。不幸的是,这些API存在一个问题:它们不完全是RESTful的,因为它们无视(至少)一个REST约束。事实上,Flickr API(以及Amazon、del.icio.us等等)的问题比我们在此讨论的要多得多,此处我们只关注那些和超媒体有关系的问题。


  幸运的是,我们不需要为寻找这些问题花太多时间。以“flickr.contacts.getList”操作返回的样本数据为例,用户可使用该操作获得他们自己的联系人清单:


  <contacts page=”1″ pages=”1″ perpage=”1000″ total=”3″>           <contact nsid=”12037949629@N01″ username=”Eric” iconserver=”1″                realname=”Eric Costello”                friend=”1″ family=”0″ ignored=”1″ />         <contact nsid=”12037949631@N01″ username=”neb” iconserver=”1″                 realname=”Ben Cerveny”                 friend=”0″ family=”0″ ignored=”0″ />         <contact   nsid=”41578656547@N01″ username=”cal_abc” iconserver=”1″                 realname=”Cal Henderson”                 friend=”1″ family=”1″   ignored=”0″ /></contacts>


  这里,“nsid”属性包含了一个表示单个联系人的唯一标识符,在这个例子里是其中的三个联系人。但是一旦客户端已经检索到了这个文档,接下来会怎样?如果他们想了解更多关于Cal Henderson的信息怎么办?通过快速检查Flickr API文档,可以发现有一个叫做”flickr.people.getInfo”的操作,它接收nsid作为一个参数,返回那个nsid字符串所标识联系人的更多信息。那么为了获得关于Cal的更多信息,我们在HTTP GET消息中需要使用的URI将是:


  http://api.flickr.com/services/rest/?method=flickr.people.getInfo?auth_key=xxxx&user_id=41578656547@N01


  这不是超媒体。一个超媒体解决方案会使用标准化的标识符——对于Web来说就是URI——而非私有的标识符,这避免了客户端在从联系人清单文档浏览到个人信息文档过程中需要特定于Flickr的知识。如果采用标准标识符,那么第一个文档应该是:


  <contacts page=”1″ pages=”1″ perpage=”1000″ total=”3″>           <contact nsid=”http://api.flickr.com/services/rest/?  method=flickr.people.getInfo?auth_key=xxxx&user_id=12037949629@N01″ username=”Eric” iconserver=”1″                 realname=”Eric Costello”                 friend=”1″ family=”0″ ignored=”1″ />         <contact   nsid=”http://api.flickr.com/services/rest/?  method=flickr.people.getInfo?auth_key=xxxx&user_id=12037949631@N01″ username=”neb” iconserver=”1″                 realname=”Ben Cerveny”                 friend=”0″ family=”0″ ignored=”0″ />         <contact   nsid=”http://api.flickr.com/services/rest/?  method=flickr.people.getInfo?auth_key=xxxx&user_id=41578656547@N01″ username=”cal_abc” iconserver=”1″                 realname=”Cal Henderson”                   friend=”1″ family=”1″ ignored=”0″ /></contacts>


  万事大吉,但是把这些变到超媒体对他们和他们的用户会有什么好处呢?


  为了从一个应用状态前进到另一个应用状态,以Flickr现在的方式,要求客户端处理特定于Flickr的知识,这是另一种形式的私有应用。它不仅是私有的,而且即使在Flickr API本身内部它也不是一个一致的模型,因为从联系人清单文档浏览到联系人个人信息文档所需要的知识(如上面所述),不同于从联系人个人信息文档浏览到联系人个人照片清单所需要的知识(它是“flickr.photos.getContactsPublicPhotos”)。这给Flickr提出了可发展性问题,因为即使对API进行简单地扩展也轻易地要求传播新知识,需要依次修改客户端代码。一个诸如搜索引擎这样的普通客户端,也不能通过这些API来给Flickr内容建立索引,因为我确信搜索引擎的维护人员——或者其他使用这个应用程序模型的人——对于每次Ficlkr扩展API都需升级他们的软件不会有太多的兴趣。再次重申,这不仅仅限于超媒体:任何标准化的应用模型都会提供同样的好处。当然,超媒体模型已经证明它自己非常流行,即使那些使用它的人们没有意识到自己所做的事。


  因此通过使用一个公共应用模型,它不仅仅是标准化的,而且总是稳定的,你可以通过准许消费者和生产者独立进化来降低两者间耦合。通过这种方法,新旧服务可以被组合在一起形成一个组合应用,新老客户端也可以合并成一个。我认为,一旦Web可以让人们简单地在一个文档中包含一个指向几年前创作的页面和该内容消费者,当我们使用它时,完全可以无缝地浏览内容而无须下载一个新版本的浏览器。这都是特意设计的,决非偶然。


  值得注意的是,Web决没有垄断超媒体的使用。Email是另一个已普及的应用,我们大家在互联网上每天都用到它,也是如此。每条Email消息包含携带发信人和收信人的Email地址的头,而拥有这些地址中的一个或者多个就足够发送另一条Email消息了。


  当我们在讨论这些鲜为人知的事实时,你可能也会有兴趣知道:Web本身一个重要方面不使用超媒体:robots.txt,亦称漫游器排除文档(robot exclusion)。它的工作方式:如果网站希望搜索引擎不要给它们的某些内容建立索引,那么就在他们的“/robots.txt” URI中简单地放置一个文件,该文件描述了哪些内容不希望被建立索引。但是,就我所知的是“几乎没有人链接到robots.txt文件”。为什么会这样?“/robots.txt”是一个固定且众所周知的位置,特别是对于搜索引擎来说:给出任何URI,它们都能从URI构建出这个站点域相应的robots.txt URI。尽管这不是超媒体,因为链接不是动态地从另一个页面发现的,搜索引擎恰恰认为这是一个优点。这不能说它是一个坏的解决方案,因为超媒体方式需要两次网络往返(一次是发现链接robots.txt的页面,另一次是抓取页面),这对于所有的当事方都是一种负担。所以这也是一个关于超媒体成本的好例子。但是要牢记这点:只有在极少情况下这(译注:即不采用超媒体)真的才是最佳方式。和robots.txt原因相同,站点地图可能也算一个;但是其他的诸如“favicon.ico”和Apple新的iPhone WebClip特性,将可能从使用超媒体上获益;例如,那些图标可以被一个图像搜索引擎搜索到,而不需要更新搜索引擎软件。


  在考虑超媒体时,另一个值得关注的技术是WADL,Web应用描述语言。尽管它自称“RESTful描述语言”,但是对它有一个重要的警告。考虑从这个从WADL文件中摘录的例子:


   <resource path=”search”>     <method name=”GET” id=”search”>       <request>         <param name=”query” type=”xsd:string” style=”query”   required=”true”/>       </request>       <response>            <representation mediaType=”application/xml” element=”yn:ResultSet”/>         <fault status=”400″ mediaType=”application/xml” element=”ya:Error”/>         </response>     </method>   </resource></resources>


  这个文件将一个“search”资源声明为一个“myservices”集合的一部分。通过声明使用HTTP GET和使用“query”参数,它描述了客户端从它选择的输入字符串构建一个URI的方式。


  表面上看,它似乎是一个完美RESTful的、基于超媒体的解决方案,非常类似HTML表单(或者URI模板)的使用方式。那么警告是什么?问题在于WADL被消费的时机。某些对基于Web的解决方案感兴趣的Web服务支持者按照他们使用WSDL的方式(作为设计时部件)使用WADL。但是,这样使用WADL,类似于在编译浏览器的时候,开发一个具有内建知识(比方说Google主页表单)的Web浏览器:如果在部署浏览器后,Google以不向后兼容的方式改变了这个表单(即,不仅仅增加一个新的可选参数),那么浏览器将无法使用那些资源/服务。使用具有WADL的超媒体约束意味着客户端应该在运行时消费WADL。所以小心地选择你的WADL工具,因为那些试图帮助你的一些工具,可能帮倒忙。


  结论


  希望超媒体约束的价值现在看起来要明显多了。但是,不仅仅如此,我真正希望的是:当你决定使用它时,你能更好地了解哪些实践是你应该避免的。


  记住:超媒体只是统一接口约束中的一员,因此对于后者更通用石蕊测试是:如果你正在开发客户端代码,而客户端代码假定它不能适用于所有资源(或者服务端“API”需要客户端的this,译注:服务端需要客户端的信息),那么你就没有使用统一接口。


  关于作者


  Mark Baker在SOA和Web服务社区大名鼎鼎,源于他对推广REST(表述性状态转移)架构风格的不断努力,以及他批评大多数标准和规范不了解什么成就并持续成就了Web的成功。

我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。

我原创,你原创,我们的内容世界才会更加精彩!

【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】

微信公众号

TechTarget微信公众号二维码

TechTarget

官方微博

TechTarget中国官方微博二维码

TechTarget中国

相关推荐