提醒:想掌握并理解REST风格的Web服务,必需要熟悉HTTP协议,不能光知道GET/POST两种方式.详细请看:
http://my.oschina.net/zhaoqian/blog/90315 web
首先向看一下REST在JavaEE里的规范JAX-RS. 数据库
JavaTM API for RESTful Web Services (JAX-RS) 1.1 标准
JAX-RS定义了部署Web服务的API,这些Web服务根据Representational State Transfer (REST)体系风格构建。
在整个Java EE产品中,要求全部Java EE Web容器支持使用JAX-RS技术的应用程序。
此规范描述了做为Servlet对服务进行部署。必须可以使用相应的部署模型来部署基于JAX-RS的应用程序,这种部署模型使用了web.xml描述符的servlet-class元素,它的名称是应用程序提供的JAX-RS ApplicationConfig抽象类的扩展类。
此规范定义了一套可选的容器管理的功能和资源,它们会在Java EE容器中使用,全部这样的特性和资源必须可用。
JAX-RS规范参见 http://jcp.org/en/jsr/detail?id=311 浏览器
上述是JavaEE6规范里的一些说明,那就是说,JAX-RS是彻底的REST风格.那么REST风格的Web服务又是什么呢?
1.REST入门
rest,即REST(Representational State Transfer表述性状态转移)是一种针对网络应用的设计和开发方式,能够下降开发的复杂性,提升系统的可伸缩性。 缓存
对于当今最多见的网络应用来讲,resource identifier是url,generic connector interface是HTTP,第4条准则就是咱们常说的url不变性。这些概念中的resouce最容易令人产生误解。resouce所指的并非数据,而是数据+特定的表现形式(representation),这也是为何REST的全名是Representational State Transfer的缘由。举个例子来讲,“本月卖得最好的10本书”和“你最喜欢的10本书”在数据上可能有重叠(有一本书即卖得好,你又喜欢),甚至彻底相同。可是它们的representation不一样,所以是不一样的resource。
REST之因此可以简化开发,是由于其引入的架构约束,好比Rails 1.2中对REST的实现默认把controller中的方法限制在7个:index、show、new、edit、create、update和destroy,这实际上就是对CURD的实现。更进一步讲,Rails(也是当今大部分网络应用)使用HTTP做为generic connector interface,HTTP则把对一个url的操做限制在了4个以内:GET、POST、PUT和DELETE。
REST之因此可以提升系统的可伸缩性,是由于它强制全部操做都是stateless的,这样就没有context的约束,若是要作分布式、作集群,就不须要考虑context的问题了。同时,它令系统能够有效地使用pool。 安全
REST对性能的另外一个提高来自其对client和server任务的分配:server只负责提供resource以及操做resource的服务,而client要根据resource中的data和representation本身作render。这就减小了服务器的开销。 ruby
两个问题:
1.如何使用REST.
2.REST和MVC的关系.
第一个问题假设REST是咱们应该采用的架构,而后讨论如何使用.
第二个问题则要说明REST和当前最广泛应用的MVC是什么关系,互补仍是取代? 服务器
第一个问题: 如何使用REST?
我感受,
REST除了给咱们带来了一个崭新的架构之外,还有一个重要的贡献是在开发系统过程当中的一种新的思惟方式:经过url来设计系统的结构.根据REST,每一个url都表明一个resource,而整个系统就是由这些resource组成的。所以,若是url是设计良好的,那么系统的结构就也应该是设计良好的。
对于非高手级的开发人员来讲,考虑一个系统如何架构老是一个很抽象的问题.
敏捷开发所提倡的Test Driven Development(测试驱动开发),其好处之一(我以为是最大的好处)就是能够经过testcase(测试用例)直观地设计系统的接口.好比在尚未建立一个class的时候就编写一个testcase,虽然设置不能经过编译,可是testcase中的方法调用能够很好地从class使用者的角度反映出须要的接口,从而为class的设计提供了直观的表现. 网络
这与在REST架构中经过url设计系统结构很是相似。虽然咱们连一个功能都没有实现,可是咱们能够先设计出咱们认为合理的url,这些url甚至不能链接到任何page或action,可是它们直观地告诉咱们:系统对用户的访问接口就应该是这样。根据这些url,咱们能够很方便地设计系统的结构。
让我在这里重申一遍:REST容许咱们经过url设计系统,就像Test Driven Development容许咱们使用testcase设计class接口同样
OK,既然url有这样的好处,那咱们就着重讨论一下如何设计url。网络应用一般都是有hierarchy的,像棵大树。咱们一般但愿url也能反映出资源的层次性。好比对于一个blog应用:/articles表示全部的文章,/articles/1表示id为1的文章,这都比较直观。遗憾的是,网络应用的资源结构永远不会如此简单。所以人们经常会问这样一个问题:RESTful的url能覆盖全部的用户请求吗?好比,login如何RESTful?search如何RESTful?
从REST的概念上来看,全部能够被抽象为资源的东东均可以使用RESTful的url。所以对于上面的两个问题,若是login和search能够被抽象为资源,那么就可使用RESTful的url.search比较简单,由于它会返回搜索结果,所以能够被抽象为资源,而且只实现index方法就能够了(只须要显示搜索结果,没有create、destory之类的东西)。
然而这里面也有一个问题:search的关键字如何传给server?
index方法显然应该使用HTTP GET,这会把关键字加到url后面,固然不符合REST的风格。
要解决这个问题,能够把每次search看做一个资源,所以要建立create和index方法,create用来在用户点击“搜索”按钮是经过HTTP POST把关键字传给server,而后index则用来显示搜索结果。
这样一来,咱们还能够记录用户的搜索历史。使用一样的方法,咱们也能够对login应用REST,即每次login动做是一个资源。
如今,咱们来看复杂一些的东东。如何用url表达“category(种类)为ruby(红色)的article(文章)”?一开始可能想到的是/category/ruby/articles,这种想法很直观。可是我以为里面的category是不须要的,咱们能够直接把“/ruby”理解为“category是ruby”,也就是说“ruby”出现的位置说明了它指的就是category。OK,/ruby/articles,单单从这个url上看,咱们能得到多少关于category的信息呢?显然category隐藏在了url后面,这样作到底好很差,应该是仁者见仁,智者见智了。对于如何表达category这样的东西,我还没想出很好的方式,你们有什么好idea,能够一块儿讨论。(TMD….表示这段理解了半天)
另外还有一种url形式,它对应到程序中的继承关系。好比product是一个父类,book和computer是其子类。那么全部产品的url应该是/products,全部书籍的url应该是/books,全部电脑的url应该是/computers。这一想法就比较直观了,并且再次验证了url能够帮助咱们进行设计的论点。
让我再说明一下个人想法:若是每一个用户需求均可以抽象为资源,那么就能够彻底使用REST。
由此看来,使用REST的关键是如何抽象资源,抽象得越精确,对REST的应用就越好。所以,如何改变咱们目前根深蒂固的基于action的思想是最重要的.(我木有action思想,个人思想是事件—通知思想.感受发展的总趋势就是原始的请求,响应,就是action.到JSF那样的事件通知,但仍然属于MVC,如今的rest就属于URL类型的抽象模式.)
有了对第一个问题的讨论,第二个问题就容易讨论多了. session
2.REST会取代MVC吗?
仍是彼此是互补关系(就像AOP对于OOP)?答案是我以为是互补.
架构
若是咱们能够把全部的用户需求均可以抽象为资源,那么MVC就能够退出历史的舞台了。若是状况相反,那么咱们就须要混合使用REST和MVC。
固然,这是很是理想的论断。可能咱们没法找到一种方法能够把全部的用户需求都抽象为资源,由于保证这种抽象的完整性(即真的是全部需求均可以)须要形式化的证实。并且即便被证实出来了,因为开发人员的能力和喜爱不一样,MVC确定也会成为很多人的首选。可是对于但愿拥抱REST的人来讲,这些都没有关系。只要你开发的系统所设计的问题域能够被合理地抽象为资源,那么REST就会成为你的开发利器。
===================================================================================================
REST关键原则
大部分对REST的介绍是以其正式的定义和背景做为开场的。但这儿且先按下不表,我先提出一个简单扼要的定义:REST定义了应该如何正确地使用(这和大多数人的实际使用方式有很大不一样)Web标准,例如HTTP和URI。若是你在设计应用程序时能坚持REST原则,那就预示着你将会获得一个使用了优质Web架构(这将让你受益)的系统。总之,五条关键原则列举以下:
为全部“事物”定义ID;
将全部事物连接在一块儿;
使用标准方法;
资源多重表述;
无状态通讯。
下面让咱们进一步审视这些原则。
1. 为全部“事物”定义ID
在这里我使用了“事物”来代替更正式准确的术语“资源”,由于一条如此简单的原则,不该该被淹没在术语当中。思考一下人们构建的系统,一般会找到一系列值得被标识的关键抽象。每一个事物都应该是可标识的,都应该拥有一个明显的ID——在Web中,表明ID的统一律念是:URI。URI构成了一个全局命名空间,使用URI标识你的关键资源意味着它们得到了一个惟1、全局的ID。
对事物使用一致的命名规则(naming scheme)最主要的好处就是你不须要提出本身的规则——而是依靠某个已被定义,在全球范围中几乎完美运行,而且能被绝大多数人所理解的规则。想一下你构建的上一个应用(假设它不是采用RESTful方式构建的)中的任意一个高级对象(high-level object),那就颇有可能看到许多从使用惟一标识中受益的用例。好比,若是你的应用中包含一个对顾客的抽象,那么我能够至关确定,用户会但愿将一个指向某个顾客的连接,能经过电子邮件发送到同事那里,或者加入到浏览器的书签中,甚至写到纸上。更透彻地讲:若是在一个相似于Amazon的在线商城中,没有用惟一的ID(一个URI)标识它的每一件商品,可想而知这将是多么可怕的业务决策。
当面对这个原则时,许多人惊讶于这是否意味着须要直接向外界暴露数据库记录(或者数据库记录ID)——自从多年以来面向对象的实践告诫咱们,要将持久化的信息做为实现细节隐藏起来以后,哪怕是刚有点想法都常会令人惊恐。可是这条原则与隐藏实现细节二者之间并无任何冲突:一般,值得被URI标识的事物——资源——要比数据库记录抽象的多。例如,一个定单资源能够由定单项、地址以及许多其它方面(可能不但愿做为单独标识的资源暴露出来)组成。标识全部值得标识的事物,领会这个观念能够进一步引导你创造出在传统的应用程序设计中不常见的资源:一个流程或者流程步骤、一次销售、一次谈判、一份报价请求——这都是应该被标识的事物的示例。一样,这也会致使建立比非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 正如我选择了建立便于阅读的URI——这是个有用的观点,尽管不是RESTful设计所必须的——应该能十分容易地推测出URI的含义:它们明显地标识着单一“数据项”。可是再往下看:
http://example*com/orders/2007/11
http://example*com/products?color=green 首先,这两个URI看起来与以前的稍有不一样——毕竟,它们不是对一件事物的标识,而是对一类事物集合的标识(假定第一个URI标识了全部在2007年11月份提交的定单,第二个则是绿颜色产品的集合)。可是这些集合自身也是事物(资源),也应该被标识。
注意,使用惟1、全局统一的命名规则的好处,既适用于浏览器中的Web应用,也适用于机对机(machine-to-machine,m2m)通讯。
来对第一个原则作下总结:使用URI标识全部值得标识的事物,特别是应用中提供的全部“高级”资源,不管这些资源表明单一数据项、数据项集合、虚拟亦或实际的对象仍是计算结果等。
2.将全部事物连接在一块儿
接下来要讨论的原则有一个有点使人惧怕的正式描述:“超媒体被看成应用状态引擎(Hypermedia as the engine of application state)”,有时简写为HATEOAS。(严格地说,这不是我说的。)这个描述的核心是超媒体概念,换句话说:是连接的思想。连接是咱们在HTML中常见的概念,可是它的用处毫不局限于此(用于人们网络浏览)。
应用程序(已经检索过文档)如何“跟随”连接检索更多的信息。固然,若是使用一个遵照专用命名规范的简单“id”属性做为连接,也是可行的——可是仅限于应用环境以内。使用URI表示连接的优雅之处在于,连接能够指向由不一样应用、不一样服务器甚至位于另外一个大陆上的不一样公司提供的资源——由于URI命名规范是全球标准,构成Web的全部资源均可以互联互通。
超媒体原则还有一个更重要的方面——应用“状态”。简而言之,实际上服务器端(若是你愿意,也能够叫服务提供者)为客户端(服务消费者)提供一组连接,使客户端能经过连接将应用从一个状态改变为另外一个状态。稍后咱们会在另外一篇文章中探究这个方面的影响;目前,只须要记住:连接是构成动态应用的很是有效的方式。
对此原则总结以下:任何可能的状况下,使用连接指引能够被标识的事物(资源)。也正是超连接造就了如今的Web。
3.使用标准方法
在前两个原则的讨论中暗含着一个假设:接收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方法。
4.资源多重表述
到目前为止咱们一直忽略了一个稍微复杂的问题:客户程序如何知道该怎样处理检索到的数据,好比做为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接口。 总结:针对不一样的需求提供资源多重表述。
5.无状态通讯
无状态通讯是我要讲到的最后一个原则。首先,须要着重强调的是,虽然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的原则仍是有好处的。