深刻浅出 REST(转)

文章讲的不错,更具体一些,对实践的指导意义更强数据库

原文:https://www.infoq.cn/article/rest-introduction/浏览器


不知你是否意识到,围绕着什么才是实现异构的应用到应用通讯的“正确”方式,一场争论正进行的如火如荼:虽然当前主流的方式明显地集中在基于 SOAP、WSDL 和 WS-* 规范的 Web Services 领域,但也有少数人用细小但洪亮的声音主张说更好的方式是 REST,表述性状态转移(REpresentational State Transfer)的简称。在本文中,我不会涉及争论的话题,而是尝试对 REST 和 RESTful HTTP 应用集成作实用性的介绍。以个人经验,有些话题一旦触及就会引来众多的讨论,当涉及到这方面话题的时候,我会深刻详细地阐述。缓存

REST 关键原则

大部分对 REST 的介绍是以其正式的定义和背景做为开场的。但这儿且先按下不表,我先提出一个简单扼要的定义:REST 定义了应该如何正确地使用(这和大多数人的实际使用方式有很大不一样)Web 标准,例如 HTTP 和 URI。若是你在设计应用程序时能坚持 REST 原则,那就预示着你将会获得一个使用了优质 Web 架构(这将让你受益)的系统。总之,五条关键原则列举以下:安全

  • 为全部“事物”定义 ID
  • 将全部事物连接在一块儿
  • 使用标准方法
  • 资源多重表述
  • 无状态通讯

下面让咱们进一步审视这些原则。服务器

为全部“事物”定义 ID

在这里我使用了“事物”来代替更正式准确的术语“资源”,由于一条如此简单的原则,不该该被淹没在术语当中。思考一下人们构建的系统,一般会找到一系列值得被标识的关键抽象。每一个事物都应该是可标识的,都应该拥有一个明显的 ID——在 Web 中,表明 ID 的统一律念是:URI。URI 构成了一个全局命名空间,使用 URI 标识你的关键资源意味着它们得到了一个惟1、全局的 ID。网络

对事物使用一致的命名规则(naming scheme)最主要的好处就是你不须要提出本身的规则——而是依靠某个已被定义,在全球范围中几乎完美运行,而且能被绝大多数人所理解的规则。想一下你构建的上一个应用(假设它不是采用 RESTful 方式构建的)中的任意一个高级对象(high-level object),那就颇有可能看到许多从使用惟一标识中受益的用例。好比,若是你的应用中包含一个对顾客的抽象,那么我能够至关确定,用户会但愿将一个指向某个顾客的连接,能经过电子邮件发送到同事那里,或者加入到浏览器的书签中,甚至写到纸上。更透彻地讲:若是在一个相似于 Amazon.com 的在线商城中,没有用惟一的 ID(一个 URI)标识它的每一件商品,可想而知这将是多么可怕的业务决策。session

当面对这个原则时,许多人惊讶于这是否意味着须要直接向外界暴露数据库记录(或者数据库记录 ID)——自从多年以来面向对象的实践告诫咱们,要将持久化的信息做为实现细节隐藏起来以后,哪怕是刚有点想法都常会令人惊恐。可是这条原则与隐藏实现细节二者之间并无任何冲突:一般,值得被 URI 标识的事物——资源——要比数据库记录抽象的多。例如,一个定单资源能够由定单项、地址以及许多其它方面(可能不但愿做为单独标识的资源暴露出来)组成。标识全部值得标识的事物,领会这个观念能够进一步引导你创造出在传统的应用程序设计中不常见的资源:一个流程或者流程步骤、一次销售、一次谈判、一份报价请求——这都是应该被标识的事物的示例。一样,这也会致使建立比非 RESTful 设计更多的持久化实体。架构

下面是一些你可能想到的 URI 的例子:app

http://example.com/customers/1234
http://example.com/orders/2007/10/776654
http://example.com/products/4554
http://example.com/processes/salary-increase-234 

正如我选择了建立便于阅读的 URI——这是个有用的观点,尽管不是 RESTful 设计所必须的——应该能十分容易地推测出 URI 的含义:它们明显地标识着单一“数据项”。可是再往下看:less

http://example.com/orders/2007/11
http://example.com/products?color=green 

首先,这两个 URI 看起来与以前的稍有不一样——毕竟,它们不是对一件事物的标识,而是对一类事物集合的标识(假定第一个 URI 标识了全部在 2007 年 11 月份提交的定单,第二个则是绿颜色产品的集合)。可是这些集合自身也是事物(资源),也应该被标识。

注意,使用惟1、全局统一的命名规则的好处,既适用于浏览器中的 Web 应用,也适用于机对机(machine-to-machine,m2m)通讯。

来对第一个原则作下总结:使用 URI 标识全部值得标识的事物,特别是应用中提供的全部“高级”资源,不管这些资源表明单一数据项、数据项集合、虚拟亦或实际的对象仍是计算结果等。

将全部事物连接在一块儿

接下来要讨论的原则有一个有点使人惧怕的正式描述:“超媒体被看成应用状态引擎(Hypermedia as the engine of application state)”,有时简写为 HATEOAS。(严格地说,这不是我说的。)这个描述的核心是超媒体概念,换句话说:是连接的思想。连接是咱们在 HTML 中常见的概念,可是它的用处毫不局限于此(用于人们网络浏览)。考虑一下下面这个虚构的 XML 片断:

<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>

若是你观察文档中 product 和 customer 的连接,就能够很容易地想象到,应用程序(已经检索过文档)如何“跟随”连接检索更多的信息。固然,若是使用一个遵照专用命名规范的简单“id”属性做为连接,也是可行的——可是仅限于应用环境以内。使用 URI 表示连接的优雅之处在于,连接能够指向由不一样应用、不一样服务器甚至位于另外一个大陆上的不一样公司提供的资源——由于 URI 命名规范是全球标准,构成 Web 的全部资源均可以互联互通。

超媒体原则还有一个更重要的方面——应用“状态”。简而言之,实际上服务器端(若是你愿意,也能够叫服务提供者)为客户端(服务消费者)提供一组连接,使客户端能经过连接将应用从一个状态改变为另外一个状态。稍后咱们会在另外一篇文章中探究这个方面的影响;目前,只须要记住:连接是构成动态应用的很是有效的方式。

对此原则总结以下:任何可能的状况下,使用连接指引能够被标识的事物(资源)。也正是超连接造就了如今的 Web。

使用标准方法

在前两个原则的讨论中暗含着一个假设:接收 URI 的应用程序能够经过 URI 明确地作一些有意义的事情。若是你在公共汽车上看到一个 URI,你能够将它输入浏览器的地址栏中并回车——可是你的浏览器如何知道须要对这个 URI 作些什么呢?

它知道如何去处理 URI 的缘由在于全部的资源都支持一样的接口,一套一样的方法(只要你乐意,也能够称为操做)集合。在 HTTP 中这被叫作动词(verb),除了两个你们熟知的(GET 和 POST)以外,标准方法集合中还包含 PUT、DELETE、HEAD 和 OPTIONS。这些方法的含义连同行为许诺都一块儿定义在 HTTP 规范之中。若是你是一名 OO 开发人员,就能够想象到 RESTful HTTP 方案中的全部资源都继承自相似于这样的一个类(采用类 Java、C#的伪语法描述,请注意关键的方法):

class Resource {
     Resource(URI u);
     Response get();
     Response post(Request r);
     Response put(Request r);
     Response delete();
} 

因为全部资源使用了一样的接口,你能够依此使用 GET 方法检索一个表述(representation)——也就是对资源的描述。由于规范中定义了 GET 的语义,因此能够确定当你调用它的时候不须要对后果负责——这就是为何能够“安全”地调用此方法。GET 方法支持很是高效、成熟的缓存,因此在不少状况下,你甚至不须要向服务器发送请求。还能够确定的是,GET 方法具备幂等性[译注:指多个相同请求返回相同的结果]——若是你发送了一个 GET 请求没有获得结果,你可能不知道缘由是请求未能到达目的地,仍是响应在反馈的途中丢失了。幂等性保证了你能够简单地再发送一次请求解决问题。幂等性一样适用于 PUT(基本的含义是“更新资源数据,若是资源不存在的话,则根据此 URI 建立一个新的资源”)和 DELETE(你彻底能够一遍又一遍地操做它,直到得出结论——删除不存在的东西没有任何问题)方法。POST 方法,一般表示“建立一个新资源”,也能被用于调用任过程,于是它既不安全也不具备幂等性。

若是你采用 RESTful 的方式暴露应用功能(若是你乐意,也能够称为服务功能),那这条原则和它的约束一样也适用于你。若是你已经习惯于另外的设计方式,则很难去接受这条原则——毕竟,你极可能认为你的应用包含了超出这些操做表达范围的逻辑。请容许我花费一些时间来让你相信不存在这样的状况。

来看下面这个简单的采购方案例子:

能够看到,例子中定义了两个服务程序(没有包含任何实现细节)。这些服务程序的接口都是为了完成任务(正是咱们讨论的 OrderManagement 和 CustomerManagement 服务)而定制的。若是客户端程序试图使用这些服务,那它必须针对这些特定接口进行编码——不可能在这些接口定义以前,使用客户程序去有目的地和接口协做。这些接口定义了服务程序的应用协议(application protocol)。

在 RESTful HTTP 方式中,你将经过组成HTTP 应用协议的通用接口访问服务程序。你可能会想出像这样的方式:

能够看到,服务程序中的特定操做被映射成为标准的 HTTP 方法——为了消除歧义,我建立了一组全新的资源。“这是骗人的把戏”,我听见你叫嚷着。不,这不是欺骗。标识一个顾客的 URI 上的 GET 方法正好至关于 getCustomerDetails 操做。有人用三角形形象化地说明了这一点:

把三个顶点想象为你能够调节的按钮。能够看到在第一种方法中,你拥有许多操做,许多种类的数据以及固定数量的“实例”(本质上和你拥有的服务程序数量一致)。在第二种方法中,你拥有固定数量的操做,许多种类的数据和许多调用固定方法的对象。它的意义在于,证实了经过这两种方式,你基本上能够表示任何你喜欢的事情。

为何使用标准方法如此重要?从根本上说,它使你的应用成为 Web 的一部分——应用程序为 Web 变成 Internet 上最成功的应用所作的贡献,与它添加到 Web 中的资源数量成比例。采用 RESTful 方式,一个应用可能会向 Web 中添加数以百万计的客户 URI;若是采用 CORBA 技术并维持应用的原有设计方式,那它的贡献大抵只是一个“端点(endpoint)”——就比如一个很是小的门,仅仅容许有钥匙的人进入其中的资源域。

统一接口也使得全部理解 HTTP 应用协议的组件能与你的应用交互。通用客户程序(generic client)就是从中受益的组件的例子,例如 curl、wget、代理、缓存、HTTP 服务器、网关还有 Google、Yahoo!、MSN 等等。

总结以下:为使客户端程序能与你的资源相互协做,资源应该正确地实现默认的应用协议(HTTP),也就是使用标准的 GET、PUT、POST 和 DELETE 方法。

资源多重表述

到目前为止咱们一直忽略了一个稍微复杂的问题:客户程序如何知道该怎样处理检索到的数据,好比做为 GET 或者 POST 请求的结果?缘由是,HTTP 采起的方式是容许数据处理和操做调用之间关系分离的。换句话说,若是客户程序知道如何处理一种特定的数据格式,那就能够与全部提供这种表述格式的资源交互。让咱们再用一个例子来阐明这个观点。利用 HTTP 内容协商(content negotiation),客户程序能够请求一种特定格式的表述:

GET /customers/1234 HTTP/1.1
Host: example.com 
Accept: application/vnd.mycompany.customer+xml  

请求的结果多是一些由公司专有的 XML 格式表述的客户信息。假设客户程序发送另一个不一样的请求,就以下面这样:

GET /customers/1234 HTTP/1.1
Host: example.com 
Accept: text/x-vcard 

结果则多是 VCard 格式的客户地址。(在这里我没有展现响应的内容,在其 HTTP Content-type 头中应该包含着关于数据类型的元数据。)这说明为何理想的状况下,资源表述应该采用标准格式——若是客户程序对 HTTP 应用协议和一组数据格式都有所“了解”,那么它就能够用一种有意义的方式与世界上任意一个 RESTful HTTP 应用交互。不幸的是,咱们不可能拿到全部东西的标准格式,可是,或许咱们能够想到在公司或者一些合做伙伴中使用标准格式来营造一个小环境。固然以上状况不只适用于从服务器端到客户端的数据,反之既然——假若从客户端传来的数据符合应用协议,那么服务器端就可使用特定的格式处理数据,而不去关心客户端的类型。

在实践中,资源多重表述还有着其它重要的好处:若是你为你的资源提供 HTML 和 XML 两种表述方式,那这些资源不只能够被你的应用所用,还能够被任意标准 Web 浏览器所用——也就是说,你的应用信息能够被全部会使用 Web 的人获取到。

资源多重表述还有另一种使用方式:你能够将应用的 Web UI 归入到 Web API 中——毕竟,API 的设计一般是由 UI 能够提供的功能驱动的,而 UI 也是经过 API 执行动做的。将这两个任务合二为一带来了使人惊讶的好处,这使得使用者和调用程序都能获得更好的 Web 接口。

总结:针对不一样的需求提供资源多重表述。

无状态通讯

无状态通讯是我要讲到的最后一个原则。首先,须要着重强调的是,虽然 REST 包含无状态性(statelessness)的观念,但这并非说暴露功能的应用不能有状态——

事实上,在大部分状况下这会致使整个作法没有任何用处。REST 要求状态要么被放入资源状态中,要么保存在客户端上。或者换句话说,服务器端不能保持除了单次请求以外的,任何与其通讯的客户端的通讯状态。这样作的最直接的理由就是可伸缩性—— 若是服务器须要保持客户端状态,那么大量的客户端交互会严重影响服务器的内存可用空间(footprint)。(注意,要作到无状态通讯每每须要须要一些从新设计——不能简单地将一些 session 状态绑缚在 URI 上,而后就宣称这个应用是 RESTful。)

但除此之外,其它方面可能显得更为重要:无状态约束使服务器的变化对客户端是不可见的,由于在两次连续的请求中,客户端并不依赖于同一台服务器。一个客户端从某台服务器上收到一份包含连接的文档,当它要作一些处理时,这台服务器宕掉了,多是硬盘坏掉而被拿去修理,多是软件须要升级重启——若是这个客户端访问了从这台服务器接收的连接,它不会察觉到后台的服务器已经改变了。

理论上的 REST

我认可:以上我所说的 REST 不是真正的 REST,并且我可能有点过多地热衷于简单化。但由于我想有一个不同凡响的开场,因此没有在一开始就介绍其正式的定义和背景。如今就让咱们稍微简要地介绍一下这方面的内容。

首先,先前我并无明确地区分 HTTP、RESTful HTTP 和 REST。要理解这些不一样方面之间的关系,咱们要先来看看 REST 的历史。

Roy T. Fielding 在他的博士学位论文(实际上你应该访问这个连接——至少对于一篇学术论文来讲,它是至关易读的。此论文已被翻译成中文)中定义了术语REST。Roy 曾是许多基本Web 协议的主要设计者,其中包括HTTP 和URIs,而且他在论文中对这些协议提出了不少想法。(这篇论文被誉为“REST 圣经”,这是恰当的——毕竟,是做者发明了这个术语,因此在定义上,他写的任何内容都被认为是权威的。)在论文中,Roy 首先定义一种方法论来谈论架构风格——高级、抽象的模式,来表达架构方法背后的核心理念。每个架构风格由一系列的约束(constraints)定义造成。架构风格的例子包括“没有风格”(根本没有任何约束)、管道和过滤器(pipe and filter)、客户端/ 服务器、分布式对象以及——你猜到它了——REST。

若是对你来讲这些听起来都太抽象了,那就对了——REST 在本质上是一个能够被许多不一样技术实现的高层次的风格,并且能够被实例化——经过为它的抽象特性赋上不一样的值。好比,REST 中包含资源和统一接口的概念——也就是说,全部资源都应该对这些相同的方法做出反应。可是 REST 并无说明是哪些方法,或者有多少方法。

REST 风格的一个“化身”即是 HTTP(以及一套相关的一套标准,好比 URI),或者稍微抽象一些:Web 架构自身。接着上面的例子,HTTP 使用 HTTP 动词做为 REST 统一接口的“实例”。因为 Fielding 是在 Web 已经(或者至少是大部分)“完善”了以后才定义的 REST 风格,有人可能会争论二者是否是 100% 的匹配。可是不管如何,总体上来讲 Web、HTTP 和 URI 仅仅是 REST 风格的一个主要实现。不过,因为 Roy Fielding 便是 REST 论文的做者,又对 Web 架构设计有过深远的影响,二者类似也在情理之中。

最后,我在前面一次又一次地使用着术语“RESTful HTTP”,缘由很简单:许多使用 HTTP 的应用由于一些理由并无遵循 REST 原则,有人会说使用 HTTP 而不遵循 REST 原则就等同于滥用 HTTP。固然这听起来有点狂热——事实上违反 REST 约束的缘由一般是,仅仅由于每一个约束带来的设计权衡可能不适合于一些特殊状况。但一般,违背 REST 约束的缘由可归咎于对其好处认知的缺少。来看一个明显的反面案例:使用 HTTP GET 调用相似于删除对象的操做,这违反了 REST 的安全约束和通常性常识(客户程序不该为此负责,服务器端开发人员大概不是有意而为之)。但在随后的文章中,我会说起更多这样或那样的对 HTTP 的滥用。

总结

本文试图对 REST(Web 架构)背后的概念提供快速的介绍。RESTful HTTP 暴露功能的方式与 RPC、分布式对象以及 Web Services 是不相同的;要真正理解这些不一样是须要一些心态的转变。无论你构建的应用是仅仅想暴露 Web UI 仍是想把 API 变成 Web 的一份子,了解下 REST 的原则仍是有好处的。

Stefan Tilkov是 InfoQ SOA 社区的首席编辑,而且是位于德国和瑞士的 innoQ公司的共同创始人、首席顾问和 REST 狂热分子首领。

查看英文原文: A Brief Introduction to REST


译者简介:苑永凯,软件设计师,毕业于山东大学。主要关注领域为 Java EE 企业应用、Java EE 中间件技术以及敏捷开发方法实践,微有心得。他的 Blog 为 http://blog.csdn.net/ai92 ,您也能够经过yuanyk[AT]gmail.com 与他联系。参与 InfoQ 中文站内容建设,请邮件至 china-editorial[at]infoq.com 

相关文章
相关标签/搜索