<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Chan Chen Coding...

    RESTful HTTP的實踐

    本文對RESTful HTTP的基礎(chǔ)原理做了一個概覽,探討了開發(fā)者在設(shè)計RESTful HTTP應(yīng)用時所面臨的典型問題,展示了如何在實踐中應(yīng)用REST架構(gòu)風(fēng)格,描述了常用的URI命名方法,討論了如何使用統(tǒng)一接口進行資源交互,何時使用PUT或POST以及如何支持非CURD操作等。


    雖然REST受Web技術(shù)的影響很深,但是理論上REST架構(gòu)風(fēng)格并非綁定在HTTP上。然而,HTTP是唯一與REST相關(guān)的實例。基于該原因,本文描述了通過HTTP實現(xiàn)的REST,通常它也被稱為RESTful HTTP。REST是一種風(fēng)格,而不是標(biāo)準(zhǔn)。因為既沒有REST RFC,也沒有REST協(xié)議規(guī)范或者類似的規(guī)定。REST架構(gòu)是Roy Fielding(他也是HTTP和URI規(guī)范的主要作者之一)在一篇論文中描述的。像REST這樣的架構(gòu)風(fēng)格通常會定義一組高層決定讓應(yīng)用程序去實現(xiàn)。所有實現(xiàn)了某種特定架構(gòu)風(fēng)格的應(yīng)用程序,都使用相同的模式,也用相同的方式使用別的架構(gòu)元素,如緩存,分布式策略等。Roy Fielding把REST定義成一種架構(gòu)風(fēng)格,其目標(biāo)是“使延遲和網(wǎng)絡(luò)交互最小化,同時使組件實現(xiàn)的獨立性和擴展性最大化”

    REST并沒有創(chuàng)造新的技術(shù),組件或服務(wù),隱藏在RESTful HTTP背后的理念是使用Web的現(xiàn)有特征和能力。RESTful HTTP定義了如何更好地使用現(xiàn)有Web標(biāo)準(zhǔn)中的一些準(zhǔn)則和約束。

    資源

    資源是REST中最關(guān)鍵的抽象概念,它們是能夠被遠(yuǎn)程訪問的應(yīng)用程序?qū)ο蟆R粋€資源就是一個標(biāo)識單位,任何可以被訪問或被遠(yuǎn)程操縱的東西都可能是一個資源。資源可以是靜態(tài)的,也就是該資源的狀態(tài)永遠(yuǎn)不會改變。相反,某些資源的狀態(tài)可能隨著時間推移呈現(xiàn)很大的可變性。這兩種類型的資源都是有效的。

    圖1中所顯示的這些類都能被很容易地映射成資源。面向?qū)ο笤O(shè)計者不容易理解把實體類(如Hotel或者Room)映射成資源,同樣他們也不太理解從控制類(如coordination,事務(wù)和某個類的控制類)到資源的映射。

    圖1:分析模型的例子

    分析模型是識別資源的一個非常好的“切入點”。然而,并非只能進行1對1映射,比如, <Hotel>.listOccupancy()操作也可以被建模成資源。此外,也有可能某些資源只表示實體的某一部分。資源設(shè)計的主要驅(qū)動力是網(wǎng)絡(luò)因素而不是對象模型。

    任何重要的資源都應(yīng)該能夠通過一個唯一的標(biāo)識被訪問。RESTful HTTP使用URI來識別資源。URI提供了Web通用的識別機制,它包含了客戶端直接與被引用的資源進行交互時需要的所有信息。

    如何命名資源標(biāo)識?

    雖然RESTful HTTP并沒有明確指出如何構(gòu)造一個URI路徑,但實際上經(jīng)常被使用的是一些特定的命名模式。URI命名模式有助于應(yīng)用程序調(diào)試和跟蹤,通常一個URI包含資源類型及其后面用于定位特定資源的標(biāo)識。這樣的URI不包括指定業(yè)務(wù)操作的動詞(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/Room/4  (c) http://localhost/hotel/656bcee2-28d2-404b-891b/Reservation/15  (d) http://localhost/hotel/656bcee2-28d2-404b-891b/Room/4/Reservation/15  (e) http://localhost/hotel/656bcee2-28d2-404b-891b/Room/4/Reservation/15v7  (f) http://localhost/hotel/656bcee2-28d2-404b-891bv12

    圖2:資源尋址的例子

    URI也可以被資源用來在資源表示(representation)之間建立關(guān)聯(lián)。例如,Hotel表示通過URI去引用已分配的Room資源,而不是使用普通的RoomID。使用普通的ID會強制調(diào)用者通過對資源的訪問去構(gòu)造URI,而調(diào)用者如果沒有主機名和基礎(chǔ)URI路徑等上下文信息是無法訪問到該資源的。

    超鏈接常被客戶端用于資源導(dǎo)航。RESTful API是超文本驅(qū)動的,這表示客戶端通過獲得一個Hotel表示,就能夠?qū)Ш降揭逊峙涞腞oom表示和Reservation表示。

    在實踐中,圖1所示的這些類經(jīng)常被映射成某種業(yè)務(wù)對象,這意味著在業(yè)務(wù)對象的整個生命周期中URI將保持不變。如果要創(chuàng)建一個新資源,則要為之分配一個新的URI。而一旦這個新資源被刪除,相應(yīng)的URI則跟著失效。如圖2中的(a),(b),(c)和(d)就是這種標(biāo)識的例子。另一方面,URI也可以用來引用資源快照,比如(e)和(f)就是對這類快照的引用,其URI中包含了一個版本標(biāo)識。

    URI還可以定位子資源,如示例中的(b),(c),(d)和(e)。通常,被聚集的對象會被映射成子資源,如Room是被Hotel聚集的。被聚集的對象通常沒有自己的生命周期,如果它的父對象被刪除,所有的被聚集對象也跟著被刪除。

    然而,如果一個子資源可以從一個父資源移動到另一個父對象, 那么在它的URI中就不應(yīng)該包含其父資源的標(biāo)識。比如圖1中的Reservation資源,它就可以被分配給另一個Room資源。如果一個Reservation資源的URI包含了其父資源Room的標(biāo)識,如(d)所示,則當(dāng)Room實例標(biāo)識改變時,如果該Reservation資源又被另一個資源引用的話,這就會出問題。為了避免無效的URI,Reservation應(yīng)該通過(c)這樣的方式進行尋址。

    通常,資源的URI是由服務(wù)器控制的。客戶端訪問資源時并不需要理解資源的URI命名空間結(jié)構(gòu)。比如,使用(c)和(d)兩個URI結(jié)構(gòu)對客戶端而言具有效果相同。

    統(tǒng)一資源接口

    為了簡化整體系統(tǒng)架構(gòu),REST架構(gòu)風(fēng)格包含了統(tǒng)一接口的概念。統(tǒng)一接口包含一組受限的良定義的操作,由它們進行資源的訪問和操作。不論什么資源,都使用相同的接口。客戶端與Hotel,Room或CreditScore等資源交互時使用的接口是一樣的。統(tǒng)一接口獨立于資源的URI,并且也不需要類似IDL的文件去描述可用的操作。

    RESTful HTTP的接口非常流行且廣為使用。它包含標(biāo)準(zhǔn)的HTTP方法如GET,PUT和POST(瀏覽器使用它發(fā)出請求并提取頁面)。不幸的是,很多開發(fā)者認(rèn)為實現(xiàn)RESTful應(yīng)用就是用一種直接使用HTTP的方式,這種理解是錯誤的。舉個例子,HTTP方法的實現(xiàn)必須要遵循HTTP規(guī)范的,而通過GET方法創(chuàng)建或修改對象是不遵守HTTP規(guī)范的。

    應(yīng)用統(tǒng)一接口

    關(guān)于何時以及如何使用不同的HTTP動詞(verb),在Fielding的論文中沒有任何表格、列表或其他方式的描述。對于大部分方法,如GET或 DELETE,通過閱讀HTTP規(guī)范就能清楚其含義,而對于POST和部分更新,就不那么容易了。在實踐中,對資源進行部分更新有好幾種方法,下文將有詳細(xì)介紹。

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

    重要
    方法

    典型用法

    典型狀態(tài)碼

    安全?

    冪等?

    GET

    - 獲取表示

    - 變更時獲取表示(緩存)

    200(OK) - 表示已在響應(yīng)中發(fā)出

    204(無內(nèi)容) - 資源有空表示

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

    303(See Other) - 其他(如,負(fù)載均衡)

    304(not modified)- 資源未更改(緩存)

    400 (bad request)- 指代壞請求(如,參數(shù)錯誤)

    404 (not found)- 資源不存在

    406 (not acceptable)- 服務(wù)端不支持所需表示

    500 (internal server error)- 通用錯誤響應(yīng)

    503 (Service Unavailable)- 服務(wù)端當(dāng)前無法處理請求

    DELETE

    - 刪除資源

    200 (OK)- 資源已被刪除

    301 (Moved Permanently)- 資源的URI已更改
    303 (See Other)- 其他,如負(fù)載均衡

    400 (bad request)- 指代壞請求t
    404 (not found)- 資源不存在
    409 (conflict)- 通用沖突

    500 (internal server error)- 通用錯誤響應(yīng)
    503 (Service Unavailable)- 服務(wù)端當(dāng)前無法處理請求

    PUT

    - 用客戶端管理的實例號創(chuàng)建一個資源

    - 通過替換的方式更新資源

    - 如果未被修改,則更新資源(樂觀鎖)

    200 (OK)- 如果已存在資源被更改
    201 (created)- 如果新資源被創(chuàng)建

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

    303 (See Other)- 其他(如,負(fù)載均衡)

    400 (bad request)- 指代壞請求

    404 (not found)- 資源不存在

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

    409 (conflict)- 通用沖突

    412 (Precondition Failed)- 前置條件失敗(如執(zhí)行條件更新時的沖突)

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

    500 (internal server error)- 通用錯誤響應(yīng)

    503 (Service Unavailable)- 服務(wù)當(dāng)前無法處理請求

    POST

    - 使用服務(wù)端管理的(自動產(chǎn)生)的實例號創(chuàng)建資源

    - 創(chuàng)建子資源

    - 部分更新資源

    - 如果沒有被修改,則不過更新資源(樂觀鎖)

    200(OK)- 如果現(xiàn)有資源已被更改
    201(created)- 如果新資源被創(chuàng)建
    202(accepted)- 已接受處理請求但尚未完成(異步處理)

    301(Moved Permanently)- 資源的URI被更新
    303(See Other)- 其他(如,負(fù)載均衡)

    400(bad request)- 指代壞請求
    404 (not found)- 資源不存在
    406 (not acceptable)- 服務(wù)端不支持所需表示
    409 (conflict)- 通用沖突
    412 (Precondition Failed)- 前置條件失敗(如執(zhí)行條件更新時的沖突)
    415 (unsupported media type)- 接受到的表示不受支持

    500 (internal server error)- 通用錯誤響應(yīng)
    503 (Service Unavailable)- 服務(wù)當(dāng)前無法處理請求

    表1:統(tǒng)一接口示例

    表示

    對資源的操縱永遠(yuǎn)是通過其表示實現(xiàn)的。資源可能永遠(yuǎn)不會在網(wǎng)絡(luò)中傳輸,相反,傳輸?shù)氖琴Y源的表示。資源的表示包括數(shù)據(jù)和描述數(shù)據(jù)的元數(shù)據(jù),例如,HTTP頭“Content-Type” 就是這樣一個元數(shù)據(jù)屬性。

    圖3展示了如何使用Java獲取表示。該例程使用了Java HTTP庫xLightweb中的HttpClient類,這個庫由作者本人維護。

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

    圖3:獲取表示的Java例程

    通過調(diào)用HTTP客戶端的call方法,一個訪問Hotel資源表示的HTTP請求就被發(fā)送出去。返回的表示如圖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&RoomURI=http%3A%2F%2Flocalhost%2Fhotel%2F 656bcee2-28d2-404b-891b%2FRoom%2F2&RoomURI=http%3A%2F%2Flocalhost%2Fhotel%2F6 56bcee2-28d2-404b-891b%2FRoom%2F1

    圖4:RESTful HTTP交互

    如何支持特定表示?

    為了避免傳輸很大的數(shù)據(jù)集,有時應(yīng)該接收表示屬性一個子集。在實現(xiàn)時,用于指定部分屬性的一種方式就是支持對指定屬性的尋址,如圖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調(diào)用只請求了一個屬性(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:多屬性過濾

    確定所需屬性的另一種方法是使用查詢參數(shù),通過它列出所請求的屬性,如圖7所示。查詢參數(shù)將用于定義查詢條件以及更復(fù)雜的過濾或查詢準(zhǔn)則。

    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:查詢字符串

    在上述例子中,服務(wù)器總是返回以application/x-www-form-urlencoded編碼的媒體類型的表示。該媒體類型將實體編碼成鍵值對列表。鍵值方法理解起來很容易,但缺點是,它不適用與更加復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。此外,這種媒體類型不支持標(biāo)量數(shù)據(jù)類型的綁定,如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); HotelHotel= (Hotel) JSONObject.toBean(jsonObject, Hotel.class);

    圖8:請求JSON表示

    通過設(shè)置“Accept”請求頭,客戶端就可以請求指定的表示編碼。圖8展示了如何對application/json類型的表示的請求。JSONlib將把圖9中顯示的返回響應(yīng)消息映射成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", "RoomURI":["http://localhost/hotel/656bcee2-28d2-404b-891b/Room/1",        "http://localhost/hotel/656bcee2-28d2-404b-891b/Room/2"]}

    圖9:JSON表示

    如何報告錯誤?

    當(dāng)服務(wù)器不支持所請求的表示時怎么辦?圖10展示了一個請求XML表示資源的HTTP交互,若服務(wù)器不支持這種表示,它將返回一個HTTP 406響應(yīng),表示拒絕處理該請求。

    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服務(wù)端程序必須根據(jù)HTTP規(guī)范返回狀態(tài)碼。狀態(tài)碼的第一個數(shù)字標(biāo)識返回類型,1xx表示臨時響應(yīng),2xx表示成功響應(yīng) ,3xx代表轉(zhuǎn)發(fā),4xx表示客戶端錯誤,5xx代表服務(wù)端錯誤。使用錯誤的響應(yīng)碼,或者總返回200響應(yīng),并在消息主體中包含特定應(yīng)用程序的響應(yīng),這兩種做法都是不好的實踐。

    客戶代理和中介也要分析返回碼。例如,xLightweb HttpClient默認(rèn)會把持久的HTTP連接保存在連接池中,當(dāng)一個HTTP交互完成時,持久化HTTP連接就應(yīng)返回到內(nèi)部連接池已備重用。而只有完好的連接才能被放回連接池,比如,若返回碼是5xx,那該連接就不會重回連接池了。

    有時某些特定的客戶端要求更簡潔的返回碼。一種方法是增加一個HTTP頭“X-Header”,用它來詳細(xì)描述HTTP狀態(tài)碼。

    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:附加狀態(tài)碼

    通常只有在進行編程問題診斷時才需要詳細(xì)的錯誤碼。盡管比起詳細(xì)的錯誤碼,HTTP狀態(tài)碼的描述性總是要差很多,但是在大多數(shù)情況下,它們對于客戶端正確處理問題已經(jīng)足夠了。另一種方法是在響應(yīng)主體中包含詳細(xì)的錯誤碼。

    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操作來創(chuàng)建一個新的Guest資源。PUT方法將封裝好的資源存放在Request-URI之下。該URI是由客戶端決定的,當(dāng)Request-URI指向某現(xiàn)存資源時,該資源將被新資源替換。基于該原因,PUT方法一般用于創(chuàng)建新資源或更新現(xiàn)有資源。然而,通過使用PUT,資源的整個狀態(tài)都會被改變,若一個請求只需要修改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方法是冪等的,冪等性意味著對于一個成功執(zhí)行的請求,不管其執(zhí)行多少次,其結(jié)果都是一致的。也就是說,只要你愿意,你可以用PUT方法對Hotel資源進行任意次更新,其結(jié)果都一樣。如果兩個PUT方法同時發(fā)生,那么只有其中之一會贏得最后的勝利并決定資源的最終狀態(tài)。刪除操作也是冪等的,如果一個PUT方法和DELETE方法同時發(fā)生,那么資源或者被更新,或者被刪除,而不可能停留在某個中間狀態(tài)。

    如果你不確定是PUT還是DELETE被成功執(zhí)行,并且沒有得到狀態(tài)碼409 (Conflict)或者 417 (Expectation Failed)的話,那么就重新執(zhí)行一遍。而不需要附加的可靠性協(xié)議來避免重復(fù)請求,因為通常重復(fù)的請求不會有任何影響。

    上述描述對于POST方法就不適用了,因為POST方法不是冪等的,若要兩次執(zhí)行同一個POST請求那就要注意了。POST方法所缺失的冪等性就解釋了為什么當(dāng)你每次重新發(fā)送POST請求時瀏覽器總是彈出警告。POST方法用于創(chuàng)建資源,而不需要由客戶端指定實例id,圖14展示了通過POST方法創(chuàng)建一個Hotel資源的HTTP交互過程。通常,客戶端使用只包含基路徑和資源類型名的URI來發(fā)送POST請求。

    REQUEST: POST /HotelHTTP/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 Hotelresource has been created

    圖14:HTTP POST交互(創(chuàng)建)

    POST方法也經(jīng)常用于更新資源的部分內(nèi)容,比如,如果我們要通過發(fā)送僅包含classification屬性的PUT請求去更新Hotel資源的話,這就是違反HTTP的,但是用POST方法則沒有問題。POST方法既不是冪等的,也不是安全的。圖15展示了一個執(zhí)行部分更新的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 Hotelresource has been updated (classification)

    圖15: HTTP POST交互 (更新)

    還可以使用PATCH方法來進行部分更新,PATCH方法是對資源進行部分更新的一個特殊方法。一個PATCH請求包含一個補丁文檔,它將應(yīng)用于由Request-URI所指定的資源。然而PATCH的RFC規(guī)范還在草稿中。

    使用HTTP緩存

    為提高擴展性并降低服務(wù)端負(fù)載, RESTful的HTTP應(yīng)用程序可以利用WEB基礎(chǔ)設(shè)施的緩存機制。HTTP已經(jīng)意識到緩存是WEB基礎(chǔ)設(shè)施必不可少的一部分,比如,HTTP協(xié)議定義了專門的消息頭來支持緩存。如果服務(wù)端設(shè)置了這個頭,客戶端(如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顯示了一個重復(fù)的GET調(diào)用。通過設(shè)置最大緩存大小的值>0激活了HttpClient的緩存功能。如果響應(yīng)消息中包含了刷新頭,比如Expires或Cache-Control: max-age,該響應(yīng)就會被HttpClient緩存。這些頭指明了關(guān)聯(lián)的表示可以保鮮的時間為多久。如果在一段時間內(nèi)發(fā)出了相同的請求,那么HttpClient就會使用緩存為這些請求提供服務(wù),而不需要重復(fù)進行網(wǎng)絡(luò)調(diào)用。在網(wǎng)絡(luò)上總共只有一次HTTP交互,如圖17所示。諸如WEB代理之類的緩存中介也實現(xiàn)了相同的功能,而且該緩存還可以在不同客戶端之間共享。

    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響應(yīng)

    過期模型在靜態(tài)資源上很好用,可是,對于動態(tài)資源(資源狀態(tài)經(jīng)常改變且無法預(yù)測)則不盡相同。HTTP通過驗證頭,如Last-Modified以及ETag來支持動態(tài)資源的緩存。與過期模型相比,驗證模型沒有節(jié)省網(wǎng)絡(luò)調(diào)用。但是,當(dāng)執(zhí)行帶條件的GET方法時它會對昂貴的操作節(jié)約網(wǎng)絡(luò)傳輸,圖 18(2.request)顯示了帶條件的GET操作,它帶有一個額外的Last-Modified頭,這個頭包含了緩存對象最后修改日期。如果該資源未被更改,服務(wù)端將會返回一個304 (Not Modified) 響應(yīng)。

    1. REQUEST: GET /hotel/656bcee2-28d2-404b-891b/Reservation/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&RoomURI=http%3A%2F%2F localhost%2Fhotel%2F656bcee2-28d2-404b-891b%2FRoom%2F1   2. REQUEST: GET /hotel/0ae526f0-9c3d/Reservation/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 GMT

    圖18:基于驗證的緩存

    不要在服務(wù)端存儲應(yīng)用狀態(tài)

    RESTful HTTP的交互必須是無狀態(tài)的,這表明每一次請求要包含處理該請求所需的一切信息。客戶端負(fù)責(zé)維護應(yīng)用狀態(tài)。RESTful服務(wù)端不需要在請求間保留應(yīng)用狀態(tài),服務(wù)端負(fù)責(zé)維護資源狀態(tài)而不是應(yīng)用狀態(tài)。服務(wù)端和中介能夠理解獨立的請求和響應(yīng)。Web緩存代理擁有一切正確處理請求所需的信息并管理它的緩存。

    這種無狀態(tài)的方法是實現(xiàn)高擴展和高可用應(yīng)用的基本原則。通常無狀態(tài)使得每一個客戶請求可以由不同的服務(wù)器來響應(yīng),當(dāng)流量增加時,新的服務(wù)器可以加進來,而如果某個服務(wù)器失敗,它也可以從集群中移除。若要了解關(guān)于負(fù)載均衡以及故障恢復(fù)方面的更詳細(xì)信息,請參考這篇文章服務(wù)器負(fù)載均衡架構(gòu) 

    對non-CRED操作的支持

    開發(fā)者經(jīng)常想了解如何將non-CRUD(Create-Read-Update-Delete)操作映射到資源。顯然,Create、Read、Update和Delete等操作能夠很容易地映射到資源的方法。然而, RESTful HTTP還不僅限于面向CRUD的應(yīng)用。

    圖19: RESTful HTTP資源

    就如圖19所示的creditScoreCheck而言,它提供了一個non-CRUD操作creditScore(...),該操作接受一個address,計算出score并返回。這樣的操作可以通過CreditScoreResource實現(xiàn),該資源代表著計算的返回。圖20展示了一個GET方法,它傳入address,然后提取CreditScoreResource表示,查詢參數(shù)被用來指定CreditScoreResource。GET方法是安全的,并且可緩存,所提它很適用于CreditScore Check的creditScore(...)方法的非功能性行為。計算的結(jié)果可以緩存一段時間,如圖20所示,響應(yīng)包含了一個緩存頭,它通知客戶端和中介執(zhí)行響應(yīng)緩存。

    REQUEST: GET /CreditScore/?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=92

    圖20:Non-CRUD HTTP GET交互

    上述例子還顯示了GET方法的局限性。盡管HTTP規(guī)范并沒有指定URL的最大長度,但是實際上客戶端,中介以及服務(wù)端對URL的長度都有限制。基于此,通過GET的查詢參數(shù)發(fā)送一個很大的實體可能會因為中介和服務(wù)器對URL長度的限制而失敗。

    另一解決方法是使用POST方法,如果作了設(shè)置,它也是可緩存的。如圖21所示,第一個POST請求的結(jié)果是創(chuàng)建了一個虛擬資源CreditScoreResource。輸入的address數(shù)據(jù)用text/card這個mime類型進行編碼,在服務(wù)端計算得到score之后,它發(fā)回一個201(created)響應(yīng),該響應(yīng)包含著所創(chuàng)建的CreditScoreResource資源的URI。 示例中還展示了如果進行了設(shè)定,POST響應(yīng)也可以被緩存。通過一個GET請求就能夠取到計算結(jié)果。GET響應(yīng)也包含一個緩存控制頭,如果客戶端緊接著重新執(zhí)行這兩次請求,那么它們都可由緩存進行響應(yīng)。

    1. REQUEST: POST /CreditScore/ 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/CreditScore/l00000001-l0000005c   the credit score resource has been created    2. REQUEST: GET /CreditScore/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=92

    圖21: Non-CRUD HTTP POST交互

    還有其他不同的實現(xiàn)方式。比如不返回201響應(yīng),而返回301(Moved Permanently)轉(zhuǎn)發(fā)響應(yīng)。該響應(yīng)缺省是可緩存的。其他避免二次請求的方法是在201響應(yīng)中增加一個新創(chuàng)建的CreditScoreResource資源的表示。

    總結(jié)

    大多數(shù)SOA架構(gòu)(如SOAP或CORBA)都試圖映射如圖1所示的類模型,或多或少是一對一的遠(yuǎn)程訪問。通常,這些SOA架構(gòu)的比較多地關(guān)注在編程語言對象的透明映射上,這種映射很容易理解,且易于跟蹤。可是,它們把對分布性和擴展性等方面的關(guān)注排在第二位。

    相反,REST架構(gòu)風(fēng)格的最主要驅(qū)動是分布性和擴展性。RESTful HTTP接口的設(shè)計是由網(wǎng)絡(luò)因素而非編程語言的綁定驅(qū)動的。 RESTful HTTP也沒有試圖去封裝很那些難隱藏的因素,如網(wǎng)絡(luò)延遲,網(wǎng)絡(luò)健壯性以及網(wǎng)絡(luò)帶寬等。

    RESTful HTTP應(yīng)用用一種直接的方式使用HTTP協(xié)議,而不需任何抽象層,也不存在REST指定的數(shù)據(jù)域,如錯誤域,安全令牌域等。RESTful HTTP應(yīng)用只使用WEB的固有能力。設(shè)計RESTful HTTP的接口意味著遠(yuǎn)程結(jié)構(gòu)的設(shè)計者必須在HTTP協(xié)議上進行思考。這通常增加了開發(fā)周期中額外步驟。

    然而,RESTful HTTP使得應(yīng)用程序?qū)崿F(xiàn)具有高擴展性,更健壯。特別是為很大用戶群提供Web應(yīng)用的公司,如 WebMailing或SocialNetworking的應(yīng)用就能從REST架構(gòu)風(fēng)格中獲益。通常,這些應(yīng)用要更快更高地擴展,而且,這些公司通常在一些低預(yù)算的基礎(chǔ)設(shè)施(基于廣泛使用的標(biāo)準(zhǔn)組件和軟件之上)上運行應(yīng)用。

    關(guān)于作者

    Gregor Roth,xLightweb HTTP庫的作者。在United Internet組織擔(dān)任軟件架構(gòu)師,該組織是最重要的歐洲因特網(wǎng)服務(wù)提供商,其產(chǎn)品有GMX, 1&1, and Web.de等。他感興趣的領(lǐng)域包括軟件和系統(tǒng)架構(gòu)、企業(yè)架構(gòu)管理、面向?qū)ο笤O(shè)計、分布式計算和開發(fā)方法論等。

    參考文獻

    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



    -----------------------------------------------------
    Silence, the way to avoid many problems;
    Smile, the way to solve many problems;

    posted on 2012-04-01 18:56 Chan Chen 閱讀(1108) 評論(0)  編輯  收藏 所屬分類: Architecture

    主站蜘蛛池模板: 日韩中文无码有码免费视频| 亚洲男人的天堂www| 野花视频在线官网免费1| 自拍偷自拍亚洲精品被多人伦好爽| 在线观看肉片AV网站免费| 亚洲中文久久精品无码1| www.91亚洲| 国产高清不卡免费在线| 国产成人高清亚洲一区91| 亚洲va国产va天堂va久久| 国内一级一级毛片a免费| a视频免费在线观看| 亚洲已满18点击进入在线观看| 亚洲女人被黑人巨大进入| 亚欧免费视频一区二区三区| 日日狠狠久久偷偷色综合免费 | 欧美亚洲精品一区二区| 亚洲AV无码久久精品成人| 日本免费网站观看| 亚欧免费无码aⅴ在线观看| 国产亚洲男人的天堂在线观看| 久久亚洲AV午夜福利精品一区| 四虎免费永久在线播放| 亚洲网站免费观看| 两个人看的www免费视频中文| 亚洲精品动漫免费二区| 久久亚洲国产成人精品性色| 亚洲?v无码国产在丝袜线观看| 国产妇乱子伦视频免费| 91成人免费福利网站在线| 国产精品亚洲lv粉色| 亚洲国产精品xo在线观看| 亚洲精品乱码久久久久久中文字幕 | 亚洲欧洲国产精品久久| 国产中文在线亚洲精品官网| 成人免费男女视频网站慢动作| 在线免费观看你懂的| 久久免费国产精品一区二区| 午夜肉伦伦影院久久精品免费看国产一区二区三区 | 免费永久看黄在线观看app| 一二三四视频在线观看中文版免费|