RESTful HTTP的实践

REST是一种风格,而不是标准。由于既没有REST RFC,也没有REST协议规范或者相似的规定。REST架构是Roy Fielding(他也是HTTP和URI规范的主要做者之一)在一篇论文中描述的。像REST这样的架构风格一般会定义一组高层决定让应用程序去实现。全部实现了某种特定架构风格的应用程序,都使用相同的模式,也用相同的方式使用别的架构元素,如缓存,分布式策略等。Roy Fielding把REST定义成一种架构风格,其目标是“使延迟和网络交互最小化,同时使组件实现的独立性和扩展性最大化”html

虽然REST受Web技术的影响很深,可是理论上REST架构风格并不是绑定在HTTP上。然而,HTTP是惟一与REST相关的实例。基于该缘由,本文描述了经过HTTP实现的REST,一般它也被称为RESTful HTTP。java

REST并无创造新的技术,组件或服务,隐藏在RESTful HTTP背后的理念是使用Web的现有特征和能力。RESTful HTTP定义了如何更好地使用现有Web标准中的一些准则和约束。web

资源

资源是REST中最关键的抽象概念,它们是可以被远程访问的应用程序对象。一个资源就是一个标识单位,任何能够被访问或被远程操纵的东西均可能是一个资源。资源能够是静态的,也就是该资源的状态永远不会改变。相反,某些资源的状态可能随着时间推移呈现很大的可变性。这两种类型的资源都是有效的。编程

图1中所显示的这些类都能被很容易地映射成资源。面向对象设计者不容易理解把实体类(如Hotel或者Room)映射成资源,一样他们也不太理解从控制类(如coordination,事务和某个类的控制类)到资源的映射。json

图1:分析模型的例子浏览器

分析模型是识别资源的一个很是好的“切入点”。然而,并不是只能进行1对1映射,好比, <Hotel>.listOccupancy()操做也能够被建模成资源。此外,也有可能某些资源只表示实体的某一部分。资源设计的主要驱动力是网络因素而不是对象模型。缓存

任何重要的资源都应该可以经过一个惟一的标识被访问。RESTful HTTP使用URI来识别资源。URI提供了Web通用的识别机制,它包含了客户端直接与被引用的资源进行交互时须要的全部信息。安全

如何命名资源标识?

虽然RESTful HTTP并无明确指出如何构造一个URI路径,但实际上常常被使用的是一些特定的命名模式。URI命名模式有助于应用程序调试和跟踪,一般一个URI包含资源类型及其后面用于定位特定资源的标识。这样的URI不包括指定业务操做的动词(verb),而只用于定位资源。图中a1给出了Hotel资源的一个示例,同一个Hotel资源也能够经过URI(a2)访问。同一资源能够被多个URI引用。服务器

(a1) http://localhost/hotel/656bcee2-28d2-404b-891b  (a2) http://127.0.0.1/hotel/656bcee2-28d2-404b-891b  (b) http://localhost/hotel/656bcee2-28d2-404b-891b//4  (c) http://localhost/hotel/656bcee2-28d2-404b-891b//15  (d) http://localhost/hotel/656bcee2-28d2-404b-891b//4//15  (e) http://localhost/hotel/656bcee2-28d2-404b-891b//4//15v7  (f) http://localhost/hotel/656bcee2-28d2-404b-891bv12RoomReservationRoomReservationRoomReservation

图2:资源寻址的例子restful

URI也能够被资源用来在资源表示(representation)之间创建关联。例如,Hotel表示经过URI去引用已分配的Room资源,而不是使用普通的RoomID。使用普通的ID会强制调用者经过对资源的访问去构造URI,而调用者若是没有主机名和基础URI路径等上下文信息是没法访问到该资源的。

超连接常被客户端用于资源导航。RESTful API是超文本驱动的,这表示客户端经过得到一个Hotel表示,就可以导航到已分配的Room表示和Reservation表示。

在实践中,图1所示的这些类常常被映射成某种业务对象,这意味着在业务对象的整个生命周期中URI将保持不变。若是要建立一个新资源,则要为之分配一个新的URI。而一旦这个新资源被删除,相应的URI则跟着失效。如图2中的(a),(b),(c)和(d)就是这种标识的例子。另外一方面,URI也能够用来引用资源快照,好比(e)和(f)就是对这类快照的引用,其URI中包含了一个版本标识。

URI还能够定位子资源,如示例中的(b),(c),(d)和(e)。一般,被汇集的对象会被映射成子资源,如Room是被Hotel汇集的。被汇集的对象一般没有本身的生命周期,若是它的父对象被删除,全部的被汇集对象也跟着被删除。

然而,若是一个子资源能够从一个父资源移动到另外一个父对象, 那么在它的URI中就不该该包含其父资源的标识。好比图1中的Reservation资源,它就能够被分配给另外一个Room资源。若是一个Reservation资源的URI包含了其父资源Room的标识,如(d)所示,则当Room实例标识改变时,若是该Reservation资源又被另外一个资源引用的话,这就会出问题。为了不无效的URI,Reservation应该经过(c)这样的方式进行寻址。

一般,资源的URI是由服务器控制的。客户端访问资源时并不须要理解资源的URI命名空间结构。好比,使用(c)和(d)两个URI结构对客户端而言具备效果相同。

统一资源接口

为了简化总体系统架构,REST架构风格包含了统一接口的概念。统一接口包含一组受限的良定义的操做,由它们进行资源的访问和操做。不论什么资源,都使用相同的接口。客户端与Hotel,Room或CreditScore等资源交互时使用的接口是同样的。统一接口独立于资源的URI,而且也不须要相似IDL的文件去描述可用的操做。

RESTful HTTP的接口很是流行且广为使用。它包含标准的HTTP方法如GET,PUT和POST(浏览器使用它发出请求并提取页面)。不幸的是,不少开发者认为实现RESTful应用就是用一种直接使用HTTP的方式,这种理解是错误的。举个例子,HTTP方法的实现必需要遵循HTTP规范的,而经过GET方法建立或修改对象是不遵照HTTP规范的。

应用统一接口

关于什么时候以及如何使用不一样的HTTP动词(verb),在Fielding的论文中没有任何表格、列表或其余方式的描述。对于大部分方法,如GET或 DELETE,经过阅读HTTP规范就能清楚其含义,而对于POST和部分更新,就不那么容易了。在实践中,对资源进行部分更新有好几种方法,下文将有详细介绍。

表1列出了大部分重要的方法GET,DELETE,PUT和POST的典型用法:

重要
方法

典型用法

典型状态码

安全?

幂等?

GET

- 获取表示

- 变动时获取表示(缓存)

200(OK) - 表示已在响应中发出

204(无内容) - 资源有空表示

301(Moved Permanently) - 资源的URI已被更新

303(See Other) - 其余(如,负载均衡)

304(not modified)- 资源未更改(缓存)

400 (bad request)- 指代坏请求(如,参数错误)

404 (not found)- 资源不存在

406 (not acceptable)- 服务端不支持所需表示

500 (internal server error)- 通用错误响应

503 (Service Unavailable)- 服务端当前没法处理请求

DELETE

- 删除资源

200 (OK)- 资源已被删除

301 (Moved Permanently)- 资源的URI已更改
303 (See Other)- 其余,如负载均衡

400 (bad request)- 指代坏请求t
404 (not found)- 资源不存在
409 (conflict)- 通用冲突

500 (internal server error)- 通用错误响应
503 (Service Unavailable)- 服务端当前没法处理请求

PUT

- 用客户端管理的实例号建立一个资源

- 经过替换的方式更新资源

- 若是未被修改,则更新资源(乐观锁)

200 (OK)- 若是已存在资源被更改
201 (created)- 若是新资源被建立

301(Moved Permanently)- 资源的URI已更改

303 (See Other)- 其余(如,负载均衡)

400 (bad request)- 指代坏请求

404 (not found)- 资源不存在

406 (not acceptable)- 服务端不支持所需表示/p>

409 (conflict)- 通用冲突

412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突)

415 (unsupported media type)- 接受到的表示不受支持

500 (internal server error)- 通用错误响应

503 (Service Unavailable)- 服务当前没法处理请求

POST

- 使用服务端管理的(自动产生)的实例号建立资源

- 建立子资源

- 部分更新资源

- 若是没有被修改,则不过更新资源(乐观锁)

200(OK)- 若是现有资源已被更改
201(created)- 若是新资源被建立
202(accepted)- 已接受处理请求但还没有完成(异步处理)

301(Moved Permanently)- 资源的URI被更新
303(See Other)- 其余(如,负载均衡)

400(bad request)- 指代坏请求
404 (not found)- 资源不存在
406 (not acceptable)- 服务端不支持所需表示
409 (conflict)- 通用冲突
412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突)
415 (unsupported media type)- 接受到的表示不受支持

500 (internal server error)- 通用错误响应
503 (Service Unavailable)- 服务当前没法处理请求

 

表1:统一接口示例

表示

对资源的操纵永远是经过其表示实现的。资源可能永远不会在网络中传输,相反,传输的是资源的表示。资源的表示包括数据和描述数据的元数据,例如,HTTP头“Content-Type” 就是这样一个元数据属性。

图3展现了如何使用Java获取表示。该例程使用了Java HTTP库xLightweb中的HttpClient类,这个库由做者本人维护。

HttpClient httpClient = new HttpClient();  IHttpRequest request = new GetRequest(centralHotelURI); IHttpResponse response = httpClient.call(request); 

图3:获取表示的Java例程

经过调用HTTP客户端的call方法,一个访问Hotel资源表示的HTTP请求就被发送出去。返回的表示如图4所示,它也包含了用于指示实体主体的多媒体类型的Content-Type头。

REQUEST: GET /hotel/656bcee2-28d2-404b-891b HTTP/1.1 Host: localhost User-Agent: xLightweb/2.6   RESPONSE: HTTP/1.1 200 OK Server: xLightweb/2.6 Content-Length: 277 Content-Type: application/x-www-form-urlencoded   classification=Comfort&name=Central&URI=http%3A%2F%2Flocalhost%2Fhotel%2F 656bcee2-28d2-404b-891b%2F%2F2&URI=http%3A%2F%2Flocalhost%2Fhotel%2F6 56bcee2-28d2-404b-891b%2F%2F1RoomRoomRoomRoom

图4:RESTful HTTP交互

如何支持特定表示?

为了不传输很大的数据集,有时应该接收表示属性一个子集。在实现时,用于指定部分属性的一种方式就是支持对指定属性的寻址,如图5所示。

REQUEST: GET /hotel/656bcee2-28d2-404b-891b/classification HTTP/1.1 Host: localhost User-Agent: xLightweb/2.6 Accept: application/x-www-form-urlencoded   RESPONSE: HTTP/1.1 200 OK Server: xLightweb/2.6 Content-Length: 26 Content-Type: application/x-www-form-urlencoded; charset=utf-8   classification=Comfort

图5:属性过滤

图5中所示的GET调用只请求了一个属性(classification),若是要请求多个属性,所请求的属性要用逗号隔开,如图6所示。

REQUEST: GET /hotel/656bcee2-28d2-404b-891b/classification,name HTTP/1.1 Host: localhost User-Agent: xLightweb/2.6 Accept: application/x-www-form-urlencoded   RESPONSE: HTTP/1.1 200 OK Server: xLightweb/2.6 Content-Length: 43 Content-Type: application/x-www-form-urlencoded; charset=utf-8   classification=Comfort&name=Central

图6:多属性过滤

肯定所需属性的另外一种方法是使用查询参数,经过它列出所请求的属性,如图7所示。查询参数将用于定义查询条件以及更复杂的过滤或查询准则。

REQUEST: GET /hotel/656bcee2-28d2-404b-891b?reqAttr=classification&reqAttr=name HTTP/1.1 Host: localhost User-Agent: xLightweb/2.6 Accept: application/x-www-form-urlencoded   RESPONSE: HTTP/1.1 200 OK Server: xLightweb/2.6 Content-Length: 43 Content-Type: application/x-www-form-urlencoded; charset=utf-8   classification=Comfort&name=Central

图7:查询字符串

在上述例子中,服务器老是返回以application/x-www-form-urlencoded编码的媒体类型的表示。该媒体类型将实体编码成键值对列表。键值方法理解起来很容易,但缺点是,它不适用与更加复杂的数据结构。此外,这种媒体类型不支持标量数据类型的绑定,如Integer,Boolean,Date等。基于这个缘由,一般使用XML,JSON或Atom来表征资源(JSON也没有定义Data类型的绑定)

HttpClient httpClient = new HttpClient();   IHttpRequest request = new GetRequest(centralHotelURI); request.setHeader("Accept", "application/json");   IHttpResponse response = httpClient.call(request);   String jsonString = response.getBlockingBody().readString(); JSONObject jsonObject = (JSONObject) JSONSerializer.toJSON(jsonString); = (Hotel) JSONObject.toBean(jsonObject, Hotel.class);HotelHotel

图8:请求JSON表示

经过设置“Accept”请求头,客户端就能够请求指定的表示编码。图8展现了如何对application/json类型的表示的请求。JSONlib将把图9中显示的返回响应消息映射成Hotel bean。

REQUEST: GET /hotel/656bcee2-28d2-404b-891b HTTP/1.1 Host: localhost User-Agent: xLightweb/2.6 Accept: application/json   RESPONSE: HTTP/1.1 200 OK Server: xLightweb/2.6 Content-Length: 263 Content-Type: application/json; charset=utf-8   {"classification":"Comfort", "name":"Central", "URI":["http://localhost/hotel/656bcee2-28d2-404b-891b//1",        "http://localhost/hotel/656bcee2-28d2-404b-891b//2"]}RoomRoomRoom

图9:JSON表示

如何报告错误?

当服务器不支持所请求的表示时怎么办?图10展现了一个请求XML表示资源的HTTP交互,若服务器不支持这种表示,它将返回一个HTTP 406响应,表示拒绝处理该请求。

REQUEST: GET /hotel/656bcee2-28d2-404b-891b HTTP/1.1 Host: localhost User-Agent: xLightweb/2.6 Accept: text/xml   RESPONSE: HTTP/1.1 406 No match for accept header Server: xLightweb/2.6 Content-Length: 1468 Content-Type: text/html; charset=iso-8859-1   <html>    <head>       <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>       <title>Error 406 No match for accept header</title>    </head>    <body>        <h2>HTTP ERROR: 406</h2><pre>No match for accept header</pre>            ...    </body> </html>

图10:不支持的表示

RESTful HTTP服务端程序必须根据HTTP规范返回状态码。状态码的第一个数字标识返回类型,1xx表示临时响应,2xx表示成功响应 ,3xx表明转发,4xx表示客户端错误,5xx表明服务端错误。使用错误的响应码,或者总返回200响应,并在消息主体中包含特定应用程序的响应,这两种作法都是很差的实践。

客户代理和中介也要分析返回码。例如,xLightweb HttpClient默认会把持久的HTTP链接保存在链接池中,当一个HTTP交互完成时,持久化HTTP链接就应返回到内部链接池已备重用。而只有无缺的链接才能被放回链接池,好比,若返回码是5xx,那该链接就不会重回链接池了。

有时某些特定的客户端要求更简洁的返回码。一种方法是增长一个HTTP头“X-Header”,用它来详细描述HTTP状态码。

REQUEST: POST /Guest/ HTTP/1.1 Host: localhost User-Agent: xLightweb/2.6 Content-Length: 94 Content-Type: application/x-www-form-urlencoded   zip=30314&lastName=Gump&street=42+Plantation+Street&firstName=Forest&country=US& city=Baytown&state=LA    RESPONSE: HTTP/1.1 400 Bad Request Server: xLightweb/2.6 Content-Length: 55 Content-Type: text/plain; charset=utf-8 X-Enhanced-Status: BAD_ADDR_ZIP   AddressException: bad zip code 99566

图11:附加状态码

一般只有在进行编程问题诊断时才须要详细的错误码。尽管比起详细的错误码,HTTP状态码的描述性老是要差不少,可是在大多数状况下,它们对于客户端正确处理问题已经足够了。另外一种方法是在响应主体中包含详细的错误码。

PUT仍是POST?

较之流行的RPC方式,HTTP方法不只仅在方法名上有所不一样,并且HTTP方法中的某些属性(如幂等性,安全性等)也扮演着重要的角色。不一样的HTTP方法的幂等性和安全性属性也是不一样的。

HttpClient httpClient = new HttpClient();   String[] params = new String[] { "firstName=Forest", 			    "lastName=Gump", 			    "street=42 Plantation Street", 			    "zip=30314", 			    "city=Baytown", 			    "state=LA", 			    "country=US"}; IHttpRequest request = new PutRequest(gumpURI, params); IHttpResponse response = httpClient.call(request);

图12:使用PUT方法

如图12和13所示,使用PUT操做来建立一个新的Guest资源。PUT方法将封装好的资源存放在Request-URI之下。该URI是由客户端决定的,当Request-URI指向某现存资源时,该资源将被新资源替换。基于该缘由,PUT方法通常用于建立新资源或更新现有资源。然而,经过使用PUT,资源的整个状态都会被改变,若一个请求只须要修改zip域,它不得不包含该资源的其余域,如 firstName,city等。

REQUEST: PUT Hotel/guest/bc45-9aa3-3f22d HTTP/1.1 Host: localhost User-Agent: xLightweb/2.6 Content-Length: 94 Content-Type: application/x-www-form-urlencoded   zip=30314&lastName=Gump&street=42+Plantation+Street&firstName=Forest&country=US& city=Baytown&state=LA    RESPONSE: HTTP/1.1 200 OK Server: xLightweb/2.6 Content-Length: 36 Content-Type: text/plain; charset=utf-8 Location: http://localhost/guest/bc45-9aa3-3f22d   The guest resource has been updated. 

图13:HTTP PUT交互

PUT方法是幂等的,幂等性意味着对于一个成功执行的请求,无论其执行多少次,其结果都是一致的。也就是说,只要你愿意,你能够用PUT方法对Hotel资源进行任意次更新,其结果都同样。若是两个PUT方法同时发生,那么只有其中之一会赢得最后的胜利并决定资源的最终状态。删除操做也是幂等的,若是一个PUT方法和DELETE方法同时发生,那么资源或者被更新,或者被删除,而不可能停留在某个中间状态。

若是你不肯定是PUT仍是DELETE被成功执行,而且没有获得状态码409 (Conflict)或者 417 (Expectation Failed)的话,那么就从新执行一遍。而不须要附加的可靠性协议来避免重复请求,由于一般重复的请求不会有任何影响。

上述描述对于POST方法就不适用了,由于POST方法不是幂等的,若要两次执行同一个POST请求那就要注意了。POST方法所缺失的幂等性就解释了为何当你每次从新发送POST请求时浏览器老是弹出警告。POST方法用于建立资源,而不须要由客户端指定实例id,图14展现了经过POST方法建立一个Hotel资源的HTTP交互过程。一般,客户端使用只包含基路径和资源类型名的URI来发送POST请求。

REQUEST: POST /HTTP/1.1 Host: localhost User-Agent: xLightweb/2.6 Content-Length: 35 Content-Type: application/x-www-form-urlencoded; charset=utf-8 Accept: text/plain   classification=Comfort&name=Central   RESPONSE: HTTP/1.1 201 Created Server: xLightweb/2.6 Content-Length: 40 Content-Type: text/plain; charset=utf-8 Location: http://localhost/hotel/656bcee2-28d2-404b-891b   the resource has been createdHotelHotel

图14:HTTP POST交互(建立)

POST方法也常常用于更新资源的部份内容,好比,若是咱们要经过发送仅包含classification属性的PUT请求去更新Hotel资源的话,这就是违反HTTP的,可是用POST方法则没有问题。POST方法既不是幂等的,也不是安全的。图15展现了一个执行部分更新的POST方法。

REQUEST: POST /hotel/0ae526f0-9c3d HTTP/1.1 Host: localhost User-Agent: xLightweb/2.6 Content-Length: 19 Content-Type: application/x-www-form-urlencoded; charset=utf-8 Accept: text/plain  classification=First+Class    RESPONSE: HTTP/1.1 200 OK Server: xLightweb/2.6 Content-Length: 52 Content-Type: text/plain; charset=utf-8   the resource has been updated (classification)Hotel

图15: HTTP POST交互 (更新)

还可使用PATCH方法来进行部分更新,PATCH方法是对资源进行部分更新的一个特殊方法。一个PATCH请求包含一个补丁文档,它将应用于由Request-URI所指定的资源。然而PATCH的RFC规范还在草稿中。

使用HTTP缓存

为提升扩展性并下降服务端负载, RESTful的HTTP应用程序能够利用WEB基础设施的缓存机制。HTTP已经意识到缓存是WEB基础设施必不可少的一部分,好比,HTTP协议定义了专门的消息头来支持缓存。若是服务端设置了这个头,客户端(如HTTP客户端或Web缓存代理)就可以有效地支持缓存策略。

HttpClient httpClient = new HttpClient(); httpClient.setCacheMaxSizeKB(500000);   IHttpRequest request = new GetRequest(centralHotelURI + "/classification"); request.setHeader("Accept", "text/plain");   IHttpResponse response = httpClient.call(request); String classification = response.getBlockingBody.readString();   // ... sometime later re-execute the request response = httpClient.call(request); classification = response.getBlockingBody.readString();

图16:客户端缓存交互

图16显示了一个重复的GET调用。经过设置最大缓存大小的值>0激活了HttpClient的缓存功能。若是响应消息中包含了刷新头,好比Expires或Cache-Control: max-age,该响应就会被HttpClient缓存。这些头指明了关联的表示能够保鲜的时间为多久。若是在一段时间内发出了相同的请求,那么HttpClient就会使用缓存为这些请求提供服务,而不须要重复进行网络调用。在网络上总共只有一次HTTP交互,如图17所示。诸如WEB代理之类的缓存中介也实现了相同的功能,并且该缓存还能够在不一样客户端之间共享。

REQUEST: GET /hotel/656bcee2-28d2-404b-891b/classification HTTP/1.1 Host: localhost User-Agent: xLightweb/2.6 Accept: text/plain   RESPONSE: HTTP/1.1 200 OK Server: xLightweb/2.6 Cache-Control: public, max-age=60 Content-Length: 26 Content-Type: text/plain; charset=utf-8   comfort

图17:包含过时头的HTTP响应

过时模型在静态资源上很好用,但是,对于动态资源(资源状态常常改变且没法预测)则不尽相同。HTTP经过验证头,如Last-Modified以及ETag来支持动态资源的缓存。与过时模型相比,验证模型没有节省网络调用。可是,当执行带条件的GET方法时它会对昂贵的操做节约网络传输,图 18(2.request)显示了带条件的GET操做,它带有一个额外的Last-Modified头,这个头包含了缓存对象最后修改日期。若是该资源未被更改,服务端将会返回一个304 (Not Modified) 响应。

1. REQUEST: GET /hotel/656bcee2-28d2-404b-891b//1 HTTP/1.1 Host: localhost User-Agent: xLightweb/2.6 Accept: application/x-www-form-urlencoded   1. RESPONSE: HTTP/1.1 200 OK Server: xLightweb/2.6 Content-Length: 252 Content-Type: application/x-www-form-urlencoded Last-Modified: Mon, 01 Jun 2009 08:56:18 GMT   from=2009-06-01T09%3A49%3A09.718&to=2009-06-05T09%3A49%3A09.718&guestURI= http%3A%2F%2Flocalhost%2Fguest%2Fbc45-9aa3-3f22d&URI=http%3A%2F%2F localhost%2Fhotel%2F656bcee2-28d2-404b-891b%2F%2F1   2. REQUEST: GET /hotel/0ae526f0-9c3d//1 HTTP/1.1 Host: localhost User-Agent: xLightweb/2.6 Accept: application/x-www-form-urlencoded If-Modified-Since: Mon, 01 Jun 2009 08:56:18 GMT   2. RESPONSE: HTTP/1.1 304 Not Modified Server: xLightweb/2.6 Last-Modified: Mon, 01 Jun 2009 08:56:18 GMTReservationRoomRoomReservation

图18:基于验证的缓存

不要在服务端存储应用状态

RESTful HTTP的交互必须是无状态的,这代表每一次请求要包含处理该请求所需的一切信息。客户端负责维护应用状态。RESTful服务端不须要在请求间保留应用状态,服务端负责维护资源状态而不是应用状态。服务端和中介可以理解独立的请求和响应。Web缓存代理拥有一切正确处理请求所需的信息并管理它的缓存。

这种无状态的方法是实现高扩展和高可用应用的基本原则。一般无状态使得每个客户请求能够由不一样的服务器来响应,当流量增长时,新的服务器能够加进来,而若是某个服务器失败,它也能够从集群中移除。若要了解关于负载均衡以及故障恢复方面的更详细信息,请参考这篇文章服务器负载均衡架构 

对non-CRED操做的支持

开发者常常想了解如何将non-CRUD(Create-Read-Update-Delete)操做映射到资源。显然,Create、Read、Update和Delete等操做可以很容易地映射到资源的方法。然而, RESTful HTTP还不只限于面向CRUD的应用。

图19: RESTful HTTP资源

就如图19所示的creditScoreCheck而言,它提供了一个non-CRUD操做creditScore(...),该操做接受一个address,计算出score并返回。这样的操做能够经过CreditScoreResource实现,该资源表明着计算的返回。图20展现了一个GET方法,它传入address,而后提取CreditScoreResource表示,查询参数被用来指定CreditScoreResource。GET方法是安全的,而且可缓存,所提它很适用于CreditScore Check的creditScore(...)方法的非功能性行为。计算的结果能够缓存一段时间,如图20所示,响应包含了一个缓存头,它通知客户端和中介执行响应缓存。

REQUEST: GET //?zip=30314&lastName=Gump&street=42+Plantation+Street& 	      firstName=Forest&country=US&city=Baytown&state=LA HTTP/1.1 Host: localhost User-Agent: xLightweb/2.6 Accept: application/x-www-form-urlencoded   RESPONSE: HTTP/1.1 200 OK Server: xLightweb/2.6 Content-Length: 31 Content-Type: application/x-www-form-urlencoded Cache-Control: public, no-transform, max-age=300   scorecard=Excellent&points=92CreditScore

图20:Non-CRUD HTTP GET交互

上述例子还显示了GET方法的局限性。尽管HTTP规范并无指定URL的最大长度,可是实际上客户端,中介以及服务端对URL的长度都有限制。基于此,经过GET的查询参数发送一个很大的实体可能会由于中介和服务器对URL长度的限制而失败。

另外一解决方法是使用POST方法,若是做了设置,它也是可缓存的。如图21所示,第一个POST请求的结果是建立了一个虚拟资源CreditScoreResource。输入的address数据用text/card这个mime类型进行编码,在服务端计算获得score以后,它发回一个201(created)响应,该响应包含着所建立的CreditScoreResource资源的URI。 示例中还展现了若是进行了设定,POST响应也能够被缓存。经过一个GET请求就可以取到计算结果。GET响应也包含一个缓存控制头,若是客户端紧接着从新执行这两次请求,那么它们均可由缓存进行响应。

1. REQUEST: POST // HTTP/1.1 Host: localhost User-Agent: xLightweb/2.6 Content-Length: 198 Content-Type: text/x-vcard Accept: application/x-www-form-urlencoded   BEGIN:VCARD VERSION:2.1 N:Gump;Forest;;;; FN:Forest Gump ADR;HOME:;;42 Plantation St.;Baytown;LA;30314;US LABEL;HOME;ENCODING=QUOTED-PRINTABLE:42 Plantation St.=0D=0A30314 Baytown=0D=0ALA US END:VCARD   1. RESPONSE: HTTP/1.1 201 Created Server: xLightweb/2.6 Cache-Control: public, no-transform, max-age=300 Content-Length: 40 Content-Type: text/plain; charset=utf-8 Location: http://localhost//l00000001-l0000005c   the credit score resource has been created    2. REQUEST: GET //l00000001-l0000005c HTTP/1.1 Host: localhost User-Agent: xLightweb/2.6   2. RESPONSE: HTTP/1.1 200 OK Server: xLightweb/2.6 Content-Length: 31 Content-Type: application/x-www-form-urlencoded Cache-Control: public, no-transform, max-age=300   scorecard=Excellent&points=92CreditScoreCreditScoreCreditScore

图21: Non-CRUD HTTP POST交互

还有其余不一样的实现方式。好比不返回201响应,而返回301(Moved Permanently)转发响应。该响应缺省是可缓存的。其余避免二次请求的方法是在201响应中增长一个新建立的CreditScoreResource资源的表示。

总结

大多数SOA架构(如SOAP或CORBA)都试图映射如图1所示的类模型,或多或少是一对一的远程访问。一般,这些SOA架构的比较多地关注在编程语言对象的透明映射上,这种映射很容易理解,且易于跟踪。但是,它们把对分布性和扩展性等方面的关注排在第二位。

相反,REST架构风格的最主要驱动是分布性和扩展性。RESTful HTTP接口的设计是由网络因素而非编程语言的绑定驱动的。 RESTful HTTP也没有试图去封装很那些难隐藏的因素,如网络延迟,网络健壮性以及网络带宽等。

RESTful HTTP应用用一种直接的方式使用HTTP协议,而不需任何抽象层,也不存在REST指定的数据域,如错误域,安全令牌域等。RESTful HTTP应用只使用WEB的固有能力。设计RESTful HTTP的接口意味着远程结构的设计者必须在HTTP协议上进行思考。这一般增长了开发周期中额外步骤。

然而,RESTful HTTP使得应用程序实现具备高扩展性,更健壮。特别是为很大用户群提供Web应用的公司,如 WebMailing或SocialNetworking的应用就能从REST架构风格中获益。一般,这些应用要更快更高地扩展,并且,这些公司一般在一些低预算的基础设施(基于普遍使用的标准组件和软件之上)上运行应用。

关于做者

Gregor Roth,xLightweb HTTP库的做者。在United Internet组织担任软件架构师,该组织是最重要的欧洲因特网服务提供商,其产品有GMX, 1&1, and Web.de等。他感兴趣的领域包括软件和系统架构、企业架构管理、面向对象设计、分布式计算和开发方法论等。

相关文章
相关标签/搜索