RESTful HTTP的實踐(轉)

add by zhj: 文章有點老了,2009年的,到如今已經六年了,不過仍是頗有參考價值的。html

另外,吐槽一下PUT method,居然容許用戶用實例號來建立,靠,這也行,實例號仍是後臺來定義比較方便,由前端指定那會把實例號搞亂啊。我我的仍是喜歡用PUT來更新,若是資源不存在,也不容許他建立資源。前端

 

翻譯原文:RESTful HTTP的實踐java

本文對RESTful HTTP的基礎原理作了一個概覽,探討了開發者在設計RESTful HTTP應用時所面臨的典型問題,展現瞭如何在實踐中應用REST架構風格,描述了經常使用的URI命名方法,討論瞭如何使用統一接口進行資源交互,什麼時候使用PUT或POST以及如何支持非CURD操做等。web

REST是一種風格,而不是標準。由於既沒有REST RFC,也沒有REST協議規範或者相似的規定。REST架構是Roy Fielding(他也是HTTP和URI規範的主要做者之一)在一篇論文中描述的。像REST這樣的架構風格一般會定義一組高層決定讓應用程序去實現。全部實現了某種特定架構風格的應用程序,都使用相同的模式,也用相同的方式使用別的架構元素,如緩存,分佈式策略等。Roy Fielding把REST定義成一種架構風格,其目標是「使延遲和網絡交互最小化,同時使組件實現的獨立性和擴展性最大化」編程

雖然REST受Web技術的影響很深,可是理論上REST架構風格並不是綁定在HTTP上。然而,HTTP是惟一與REST相關的實例。基於該緣由,本文描述了經過HTTP實現的REST,一般它也被稱爲RESTful HTTP。json

REST並無創造新的技術,組件或服務,隱藏在RESTful HTTP背後的理念是使用Web的現有特徵和能力。RESTful HTTP定義瞭如何更好地使用現有Web標準中的一些準則和約束。瀏覽器

資源

資源是REST中最關鍵的抽象概念,它們是可以被遠程訪問的應用程序對象。一個資源就是一個標識單位,任何能夠被訪問或被遠程操縱的東西均可能是一個資源。資源能夠是靜態的,也就是該資源的狀態永遠不會改變。相反,某些資源的狀態可能隨着時間推移呈現很大的可變性。這兩種類型的資源都是有效的。緩存

圖1中所顯示的這些類都能被很容易地映射成資源。面向對象設計者不容易理解把實體類(如Hotel或者Room)映射成資源,一樣他們也不太理解從控制類(如coordination,事務和某個類的控制類)到資源的映射。安全

 

圖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:資源尋址的例子

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等。他感興趣的領域包括軟件和系統架構、企業架構管理、面向對象設計、分佈式計算和開發方法論等。

參考文獻

Architectural Styles and the Design of Network-based Software Architectures

REST Eye for the SOA Guy 

Presentation: Steve Vinoski on REST, Reuse and Serendipity

A Brief Introduction to REST

Fallacies of Distributed Computing

Server load balancing architectures

Asynchronous HTTP and Comet architectures

JSON-lib

xLightweb

查看英文原文RESTful HTTP in practic


感謝胡鍵對本文的審校。

給InfoQ中文站投稿或者參與內容翻譯工做,請郵件至editors@cn.infoq.com。也歡迎你們加入到InfoQ中文站用戶討論組中與咱們的編輯和其餘讀者朋友交流。

相關文章
相關標籤/搜索