作?Stefan Tilkov译?苑永?/strong> 发布?2007q?2?5?下午10?0?
不知你是否意识到Q围l着什么才是实现异构的应用到应用通信?#8220;正确”方式Q一Z论正q行的如火如|虽然当前L的方式明昑֜集中在基? SOAP、WSDL和WS-*规范的Web Services领域Q但也有数人用l小但洪亮的声音d说更好的方式是RESTQ表q性状态{U(REpresentational State TransferQ的U。在本文中,我不会涉及争论的话题Q而是试对REST和RESTful HTTP应用集成做实用性的介绍。以我的l验Q有些话题一旦触及就会引来众多的讨论Q当涉及到这斚w话题的时候,我会深入详细地阐q?/p>
大部分对REST的介l是以其正式的定义和背景作ؓ开场的。但q儿且先按下不表Q我先提Z个简单扼要的定义QREST定义了应该如何正地使用 Q这和大多数人的实际使用方式有很大不同)Web标准Q例如HTTP和URI。如果你在设计应用程序时能坚持REST原则Q那预C着你将会得C个? 了优质Web架构Q这让你受益)的系l。MQ五条关键原则列丑֦下:
下面让我们进一步审视这些原则?/p>
在这里我使用?#8220;事物”来代替更正式准确的术?#8220;资源”Q因Z条如此简单的原则Q不应该被没在术语当中。思考一下h们构建的pȝQ通常会找C pd值得被标识的关键抽象。每个事物都应该是可标识的,都应该拥有一个明昄ID——在Web中,代表ID的统一概念是:URI。URI构成了一个全局? 名空_使用URI标识你的关键资源意味着它们获得了一个唯一、全局的ID?
对事物用一致的命名规则Qnaming schemeQ最主要的好处就是你不需要提q规则——而是依靠某个已被定义Q在全球范围中几乎完运行,q且能被l大多数人所理解的规则。想一下你 构徏的上一个应用(假设它不是采用RESTful方式构徏的)中的L一个高U对象(high-level objectQ,那就很有可能看到许多从用唯一标识中受益的用例。比如,如果你的应用中包含一个对֮的抽象,那么我可以相当肯定,用户会希望将一个指 向某个顾客的链接Q能通过电子邮g发送到同事那里Q或者加入到览器的书签中,甚至写到U怸。更透彻地讲Q如果在一个类gAmazon.com的在U商 城中Q没有用唯一的IDQ一个URIQ标识它的每一件商品,可想而知q将是多么可怕的业务决策?/p>
当面对这个原则时Q许多h惊讶于这是否意味着需要直接向外界暴露数据库记录(或者数据库记录IDQ——自从多q以来面向对象的实践告诫我们Q要持 久化的信息作为实现细节隐藏v来之后,哪怕是刚有Ҏ法都怼使h惊恐。但是这条原则与隐藏实现l节两者之间ƈ没有M冲突Q通常Q值得被URI标识的事 物——资源——要比数据库记录抽象的多。例如,一个定单资源可以由定单V地址以及许多其它斚wQ可能不希望作ؓ单独标识的资源暴露出来)l成。标识所? 值得标识的事物,领会q个观念可以q一步引g创造出在传l的应用E序设计中不常见的资源:一个流E或者流E步骤、一ơ销售、一ơ谈判、一份报仯求—? q都是应该被标识的事物的CZ。同Pq也会导致创建比非RESTful设计更多的持久化实体?
下面是一些你可能惛_的URI的例子:
http://example.com/customers/1234
http://example.com/orders/2007/10/776654
http://example.com/products/4554
http://example.com/processes/salary-increase-234
正如我选择了创Z于阅ȝURI——这是个有用的观点,管不是RESTful设计所必须的——应该能十分Ҏ地推出URI的含义:它们明显地标识着单一“数据?#8221;。但是再往下看Q?/p>
http://example.com/orders/2007/11
http://example.com/products?color=green
首先Q这两个URI看v来与之前的稍有不同——毕竟,它们不是对一件事物的标识Q而是对一cM物集合的标识Q假定第一个URI标识了所有在2007q?1月䆾提交的定单,W二个则是绿颜色产品的集合)。但是这些集合自w也是事物(资源Q,也应该被标识?/p>
注意Q用唯一、全局l一的命名规则的好处Q既适用于浏览器中的Web应用Q也适用于机ҎQmachine-to-machineQm2mQ通信?/p>
来对W一个原则做下ȝQ用URI标识所有值得标识的事物,特别是应用中提供的所?#8220;高”资源Q无些资源代表单一数据V数据项集合、虚拟亦或实际的对象q是计算l果{?/p>
接下来要讨论的原则有一个有点o人害怕的正式描述Q?#8220;媒体被当作应用状态引擎(Hypermedia as the engine of application stateQ?#8221;Q有时简写ؓHATEOAS。(严格地说Q这不是我说的。)q个描述的核心是媒?/strong>概念Q换句话_?strong>链接的思想。链接是我们在HTML中常见的概念Q但是它的用处绝不局限于此(用于Z|络览Q。考虑一下下面这个虚构的XML片段Q?
如果你观察文档中product和customer的链接,可以很Ҏ地想象到Q应用程序(已经索过文档Q如?#8220;跟随”链接索更多的信息。当Ӟ如果使用一个遵守专用命名规范的?#8220;id”属性作为链接, 也是可行的—?strong>但是仅限于应用环境之?/strong>。用URI表示链接的优雅之处在于,链接可以指向׃同应用、不同服务器甚至位于另一个大陆上的不同公司提供的资源——因为URI命名规范是全球标准,构成Web的所有资源都可以互联互通?<order self="http://example.com/customers/1234">
<amount>23</amount>
<product ref="http://example.com/products/4554">
<customer ref="http://example.com/customers/1234">
</customer>
</product>
</order>
媒体原则还有一个更重要的方面——应?#8220;状?#8221;。简而言之,实际上服务器端(如果你愿意,也可以叫服务提供者)为客LQ服务消费者)提供一l链 接,使客L能通过链接应用从一个状态改变ؓ另一个状态。稍后我们会在另一文章中探究q个斚w的媄响;目前Q只需要记住:链接是构成动态应用的非常? 效的方式?/p>
Ҏ原则ȝ如下QQ何可能的情况下,使用链接指引可以被标识的事物Q资源)。也正是链接造就了现在的Web?/p>
在前两个原则的讨Z暗含着一个假设:接收URI的应用程序可以通过URI明确?strong>?/strong>一些有意义的事情。如果你在公共汽车上看到一个URIQ你可以它输入览器的地址栏中q回车——但是你的浏览器如何知道需要对q个URI做些什么呢Q?/p>
它知道如何去处理URI的原因在于所有的资源都支持同L接口Q一套同LҎQ只要你乐意Q也可以UCؓ操作Q集合。在HTTP中这被叫做动? QverbQ,除了两个大家熟知的(GET和POSTQ之外,标准Ҏ集合中还包含PUT、DELETE、HEAD和OPTIONS。这些方法的含义q同 行ؓ许诺都一起定义在HTTP规范之中。如果你是一名OO开发h员,可以想象到RESTful HTTPҎ中的所有资源都l承自类gq样的一个类Q采用类Java、C#的伪语法描述Q请注意关键的方法)Q?/p>
class Resource {
Resource(URI u);
Response get();
Response post(Request r);
Response put(Request r);
Response delete();
}
׃所有资源用了同样的接口,你可以依此用GETҎ索一?strong>表述QrepresentationQ——也
是对资源的描述。因范中定义了GET的语义,所以可以肯定当你调用它的时候不需要对后果负责——这是Z么可?#8220;安全”地调用此Ҏ。GETҎ
支持非常高效、成熟的~存Q所以在很多情况下,你甚至不需要向服务器发送请求。还可以肯定的是QGETҎhq等?/strong>[?
注:指多个相同请求返回相同的l果]——如果你发送了一个GETh没有得到l果Q你可能不知道原因是h未能到达目的圎ͼq是响应在反馈的途中丢失了?
q等性保证了你可以简单地再发送一ơ请求解决问题。幂{性同样适用于PUTQ基本的含义?#8220;更新资源数据Q如果资源不存在的话Q则Ҏ此URI创徏一个新
的资?#8221;Q和DELETEQ你完全可以一遍又一遍地操作它,直到得出l论——删除不存在的东西没有Q何问题)Ҏ。POSTҎQ通常表示“创徏一个新?
?#8221;Q也能被用于调用Lq程Q因而它既不安全也不hq等性?/p>
如果你采用RESTful的方式暴露应用功能(如果你乐意,也可以称为服务功能)Q?strong>那这条原则和它的U束同样也适用于你
来看下面q个单的采购Ҏ例子Q?
可以看到Q例子中定义了两个服务程序(没有包含M实现l节Q。这些服务程序的接口都是Z完成dQ正是我们讨论的 OrderManagement和CustomerManagement服务Q而定制的。如果客LE序试图使用q些服务Q那它必针对这些特定接口进? ~码——不可能在这些接口定义之前,使用客户E序L目的地和接口协作。这些接口定义了服务E序的应用协议(application protocolQ?/p>
在RESTful HTTP方式中,你将通过l成HTTP应用协议的通用接口讉K服务E序。你可能会想出像q样的方式:
可以看到Q服务程序中的特定操作被映射成ؓ标准的HTTPҎ——ؓ了消除歧义,我创Z一l全新的资源?#8220;q是骗h的把?#8221;Q我听见你叫L? 不,q不是欺骗。标识一个顾客的URI上的GETҎ正好相当于getCustomerDetails操作。有人用三角形Ş象化地说明了q一点:
把三个顶Ҏ象ؓ你可以调节的按钮。可以看到在W一U方法中Q你拥有许多操作Q许多种cȝ数据以及固定数量?#8220;实例”Q本质上和你拥有的服务程序数 量一_。在W二U方法中Q你拥有固定数量的操作,许多U类的数据和许多调用固定Ҏ的对象。它的意义在于,证明了通过q两U方式,你基本上可以表示M 你喜Ƣ的事情?/p>
Z么用标准方法如此重要?从根本上_它你的应用成ؓWeb的一部分——应用程序ؓWeb变成Internet上最成功的应用所做的贡献Q与 它添加到Web中的资源数量成比例。采用RESTful方式Q一个应用可能会向Web中添加数以百万计的客户URIQ如果采用CORBA技术ƈl持应用? 原有设计方式Q那它的贡献大抵只是一?#8220;端点QendpointQ?#8221;——就好比一个非常小的门Q仅仅允许有钥匙的hq入其中的资源域?/p>
l一接口也得所有理解HTTP应用协议的组件能与你的应用交互。通用客户E序Qgeneric clientQ就是从中受益的lg的例子,例如curl、wget、代理、缓存、HTTP服务器、网兌有Google、Yahoo!、MSN{等?
ȝ如下Qؓ使客LE序能与你的资源怺协作Q资源应该正地实现默认的应用协议(HTTPQ,也就是用标准的GET、PUT、POST和DELETEҎ?/p>
到目前ؓ止我们一直忽略了一个稍微复杂的问题Q客L序如何知道该怎样处理索到的数据,比如作ؓGET或者POSTh的结果?原因是,HTTP 采取的方式是允许数据处理和操作调用之间关pdȝ。换句话_如果客户E序知道如何处理一U特定的数据格式Q那可以与所有提供这U表q格式的资源? 互。让我们再用一个例子来阐明q个观点。利用HTTP内容协商Qcontent negotiationQ,客户E序可以h一U特定格式的表述Q?/p>
GET /customers/1234 HTTP/1.1
Host: example.com
Accept: application/vnd.mycompany.customer+xml
h的结果可能是一些由公司专有的XML格式表述的客户信息。假讑֮L序发送另外一个不同的hQ就如下面这P
GET /customers/1234 HTTP/1.1
Host: example.com
Accept: text/x-vcard
l果则可能是VCard格式的客户地址。(在这里我没有展示响应的内容,在其HTTP Content-type头中应该包含着关于数据cd的元数据。)q说明ؓ什么理想的情况下,资源表述应该采用标准格式——如果客L序对HTTP应用? 议和一l数据格式都有所“了解”Q那么它可以用一U有意义的方?strong>与世界上L一个RESTful HTTP应用交互? 不幸的是Q我们不可能拿到所有东西的标准格式Q但是,或许我们可以惛_在公司或者一些合作伙伴中使用标准格式来营造一个小环境。当然以上情况不仅适用于从 服务器端到客L的数据,反之既然——倘若从客L传来的数据符合应用协议,那么服务器端可以用特定的格式处理数据Q而不d心客L的类型?
在实践中Q资源多重表q还有着其它重要的好处:如果你ؓ你的资源提供HTML和XML两种表述方式Q那q些资源不仅可以被你的应用所用,q可以被L标准Web览器所用——也是_你的应用信息可以被所有会使用Web的h获取到?/p>
资源多重表述q有另外一U用方式:你可以将应用的Web UIU_到Web API中——毕竟,API的设计通常是由UI可以提供的功能驱动的Q而UI也是通过API执行动作的。将q两个Q务合二ؓ一带来了o人惊讶的好处Q这使得 使用者和调用E序都能得到更好的Web接口?/p>
ȝQ针对不同的需求提供资源多重表q?/p>
无状态通信是我要讲到的最后一个原则。首先,需要着重强调的是,虽然REST包含无状态性(statelessnessQ的观念Q但qƈ不是说暴露功能的应用不能有状态—?br /> 事实上,在大部分情况下这会导致整个做法没有Q何用处。REST要求状态要么被攑օ资源状态中Q要么保存在客户端上。或者换句话_服务器端不能保持除了 单次h之外的,M与其通信的客L的通信状态。这样做的最直接的理由就是可伸羃性—? 如果服务器需要保持客L状态,那么大量的客L交互会严重媄响服务器的内存可用空_footprintQ。(注意Q要做到无状态通信往往需要需要一? 重新设计——不能简单地一些session状态绑~在URI上,然后宣U这个应用是RESTful。)
但除此以外,其它斚w可能昑־更ؓ重要Q无状态约束服务器的变化对客L是不可见的,因ؓ在两ơ连l的h中,客户端ƈ不依赖于同一台服务器。一 个客L从某台服务器上收C份包含链接的文档Q当它要做一些处理时Q这台服务器宕掉了,可能是硬盘坏掉而被拿去修理Q可能是软g需要升U重启——如果这 个客L讉K了从q台服务器接收的链接Q它不会察觉到后台的服务器已l改变了?/p>
我承认:以上我所说的REST不是真正的RESTQ而且我可能有点过多地热衷于简单化。但因ؓ我想有一个与众不同的开场,所以没有在一开始就介绍其正式的定义和背景。现在就让我们稍微简要地介绍一下这斚w的内宏V?/p>
首先Q先前我q没有明地区分HTTP、RESTful HTTP和REST。要理解q些不同斚w之间的关p,我们要先来看看REST的历双Ӏ?/p>
Roy T. Fielding在他?a id="ewd-" title="博士学位论文" >博士学位论文Q实际上你应该访问这个链接——至对于一学术论文来_它是相当易读的。此论文已被译?a id="nev8" title="中文" >中文Q? 中定义了术语REST。Roy曾是许多基本Web协议的主要设计者,其中包括HTTP和URIsQƈ且他在论文中对这些协议提Z很多x。(q篇论文? 誉ؓ“REST圣经”Q这是恰当的——毕竟,是作者发明了q个术语Q所以在定义上,他写的Q何内定w被认为是权威的。)在论文中QRoy首先定义一U方? 论来谈论架构风格——高U、抽象的模式Q来表达架构Ҏ背后的核心理c每一个架构风格由一pd?strong>U束QconstraintsQ定义Ş成。架构风格的例子包括“没有风格”Q根本没有Q何约束)、管道和qo器(pipe and filterQ、客L/服务器、分布式对象以及——你猜到它了——REST?/p>
如果对你来说q些听v来都太抽象了Q那对了——REST在本质上是一个可以被许多不同技术实现的高层ơ的风格Q而且可以被实例化——通过为它的抽 象特性赋上不同的倹{比如,REST中包含资源和l一接口的概念——也是_所有资源都应该对这些相同的Ҏ作出反应。但是RESTq没有说明是哪些? 法,或者有多少Ҏ?/p>
REST风格的一?#8220;化n”便是HTTPQ以及一套相关的一套标准,比如URIQ,或者稍微抽象一些:Web架构自n。接着上面的例子,HTTP? 用HTTP动词作ؓRESTl一接口?#8220;实例”。由于Fielding是在Web已经Q或者至是大部分)“完善”了之后才定义的REST风格Q有人可? 会争Z者是不是100%的匹配。但是无论如何,整体上来说Web、HTTP和URI仅仅是REST风格的一个主要实现。不q,׃Roy FieldingxREST论文的作者,又对Web架构设计有过p的媄响,两者相g在情理之中?/p>
最后,我在前面一ơ又一ơ地使用着术语“RESTful HTTP”Q原因很单:许多使用HTTP的应用因Z些理由ƈ没有遵@REST原则Q有Z说用HTTP而不遵@REST原则q同于滥用HTTP? 当然q听h有点狂热——事实上q反RESTU束的原因通常是,仅仅因ؓ每个U束带来的设计权衡可能不适合于一些特D情c但通常Q违背RESTU束的原 因可归咎于对其好处认知的~Z。来看一个明昄反面案例Q用HTTP GET调用cM于删除对象的操作Q这q反了REST的安全约束和一般性常识(客户E序不应为此负责Q服务器端开发h员大概不是有意而ؓ之)。但在随后的? 章中Q我会提及更多这h那样的对HTTP的滥用?/p>
本文试图对RESTQWeb架构Q背后的概念提供快速的介绍。RESTful HTTP暴露功能的方式与RPC、分布式对象以及Web Services是不相同的;要真正理解这些不同是需要一些心态的转变。不你构徏的应用是仅仅x露Web UIq是xAPI变成Web的一份子Q了解下REST的原则还是有好处的?/p>
Stefan Tilkov是InfoQ SOAC的首席编辑,q且是位于d国和瑞士?/strong>innoQ公司的共同创始h、首席顾问和REST狂热分子首领?/strong>
查看英文原文Q?a id="mxm5" title="A Brief Introduction to REST" >A Brief Introduction to REST