1. 程式人生 > >HTTP Method 詳細解讀(`GET` `HEAD` `POST` `OPTIONS` `PUT` `DELETE` `TRACE` `CONNECT`)--轉

HTTP Method 詳細解讀(`GET` `HEAD` `POST` `OPTIONS` `PUT` `DELETE` `TRACE` `CONNECT`)--轉

www. 混淆 quora post 應用層協議 code 兩種 不存在 獲得

前言

HTTP Method的歷史:

  1. HTTP 0.9 這個版本只有GET方法
  2. HTTP 1.0 這個版本有GET HEAD POST這三個方法
  3. HTTP 1.1 這個版本是當前版本,包含GET HEAD POST OPTIONS PUT DELETE TRACE CONNECT這8個方法

我們先看看HTTP 1.1 規範的中文翻譯

方法定義(Method Definitions)

HTTP/1.1常用方法的定義如下。雖然方法可以被展開,但新加的方法不能認為能分享與擴展的客戶端和服務器同樣的語義。

Hst請求頭域(見13.23節)必須能在所有的HTTP/1.1請求裏出現。

9.1 安全和等冪(Idempotent)方法

9.1.1安全方法(Safe Methods)

實現者應當知道軟件是代表用戶在互聯網上進行交互,並且應該小心地允許用戶知道任何它們可能采取的動作(action),這些動作可能給他們自己或他人帶來無法預料的結果。

特別的,GET和HEAD方法僅僅應該獲取資源而不是執行動作(action)。這些方法應該被考慮是“安全”的。可以讓用戶代理用其他的方法,如:POST,PUT,DELETE,這樣用戶代理就能知道這些方法可能會執行不安全的動作。

自然的,保證當服務器由於執行GET請求而不能產生副作用是不可能的;實際上,一些動態的資源會考慮這個特性。用戶並沒有請求這些副作用,因此不需要對這些副作用負責。

9.1.2等冪方法(Idempotent Mehtods)

方法可以有等冪的性質因為(除了出錯或終止問題)N>0個相同請求的副作用同單個請求的副作用的效果是一樣(譯註:等冪就是值不變性,相同的請求得到相同的響應結果,不會出現相同的請求出現不同的響應結果)。方法GET,HEAD,PUT,DELETE都有這種性質。同樣,方法OPTIONS和TRACE不應該有副作用,因此具有內在的等冪性。然而,有可能幾個請求的序列是不等冪的,即使在那樣的序列中所有方法都是等冪的。(如果整個序列整體的執行的結果總是相同的,並且此結果不會因為序列的整體,部分的再次執行而改變,那麽此序列是等冪的。)例如,一個序列是非等冪的如果它的結果依賴於一個值,此值在以後相同的序列裏會改變。

根據定義,一個序列如果沒有副作用,那麽此序列是等冪的(假設在資源集上沒有並行的操作)。

9.2 OPTIONS(選項)

OPTIONS方法表明請求想得到請求/響應鏈上關於此請求裏的URI(Request-URI)指定資源的通信選項信息。此方法允許客戶端去判定請求資源的選項和/或需求,或者服務器的能力,而不需要利用一個資源動作(譯註:使用POST,PUT,DELETE方法)或一個資源獲取(譯註:用GET方法)方法。

這種方法的響應是不能緩存的.。

如果OPTIONS請求消息裏包括一個實體主體(當請求消息裏出現Content-Length或者Transfer-Encoding頭域時),那麽媒體類型必須通過Content-Type頭域指明。雖然此規範沒有定義如何使用此實體主體,將來的HTTP擴展可能會利用OPTIONS請求的消息主體去得到服務器得更多信息。一個服務器如果不支持OPTION請求的消息主體,它會遺棄此請求消息主體。

如果請求URI是一個星號(‘‘),,OPTIONS請求將會應用於服務器的所有資源而不是特定資源。因為服務器的通信選項通常依賴於資源,所以””請求只能在“ping”或者“no-op”方法時才有用;它幹不了任何事情除了允許客戶端測試服務器的能力。例如:它能被用來測試代理是否遵循HTTP/1.1。

如果請求URI不是一個星號(‘*‘),,OPTIONS請求只能應用於請求URI指定資源的選項。

200響應應該包含任何指明選項性質的頭域,這些選項性質由服務器實現並且只適合那個請求的資源(例如,Allow頭域),但也可能包一些擴展的在此規範裏沒有定義的頭域。如果有響應主體的話也應該包含一些通信選項的信息。這個響應主體的格式並沒有在此規範裏定義,但是可能會在以後的HTTP裏定義。內容協商可能被用於選擇合適的響應格式。如果沒有響應主體包含,響應就應該包含一個值為“0”的Content-Length頭域。

Max-Forwards請求頭域可能會被用於針對請求鏈中特定的代理。當代理接收到一個OPTIONS請求,且此請求的URI為absoluteURI,並且此請求是可以被轉發的,那麽代理必須要檢測Max-Forwards頭域。如果Max-Forwards頭域的值為“0”,那麽此代理不能轉發此消息;而是,代理應該以它自己的通信選項響應。如果Max-Forwards頭域是比0大的整數值,那麽代理必須遞減此值當它轉發此請求時。如果沒有Max-Forwards頭域出現在請求裏,那麽代理轉發此請求時不能包含Max-Forwards頭域。

9.3 GET

GET方法意思是獲取被請求URI(Request-URI)指定的信息(以實體的格式)。如果請求URI涉及到一個數據生成過程,那麽這個生成的數據應該被作為實體在響應中返回,但這並不是過程的資源文本,除非資源文本恰好是過程的輸出(譯註:URI指示的資源是動態生成的)。

如果請求消息包含 If-Modified-Since,,If-Unmodified-Since,If-Match,,If-None-Match,或者 If-Range頭域,,GET的語義將變成“條件(conditionall) GET”。一個條件GET方法會請求滿足條件頭域的實體。條件GET方法的目的是為了減少不必要的網絡使用,這通過利用緩存的實體的更新,從而不用多次請求或傳輸客戶已經擁有的數據。.

如果請求方法包含一個Range頭域,那麽GET方法就變成“部分Get”方法。一個部分GET會請求實體的一部分,這在14.35節裏描述了。 部分GET方法的目的是為了減少不必要的網絡使用,這通過允許獲取部分實體,從而不需要傳輸客戶端已經擁有的數據。

GET請求的響應是可緩存的(cacheable)如果此響應滿足第13節HTTP緩存的要求。

看15.1.3節關於GET請求用於表單時安全考慮。

9.4 HEAD

HEAD方法和GET方法一致,除了服務器不能在響應裏返回消息主體。HEAD請求響應裏HTTP頭域裏的元信息應該和GET請求響應裏的元信息一致。此方法被用來獲取請求實體的元信息而不需要傳輸實體主體(entity-body)。此方法經常被用來測試超文本鏈接的有效性,可訪問性,和最近的改變。.

HEAD請求的響應是可緩存的,因為響應裏的信息可能被用於更新以前的那個資源的緩存實體.。如果出現一個新的域值指明了緩存實體和當前源服務器上實體的不同(可能因為Content-Length,Content-MD5,ETag或Last-Modified值的改變),那麽緩存(cache)必須認為此緩存項是過時的(stale)。

9.5 POST

POST 方法被用於請求源服務器接受請求中的實體作為請求資源的一個新的從屬物。POST被設計涵蓋下面的功能。

-已存在的資源的註釋;

-發布消息給一個布告板,新聞組,郵件列表,或者相似的文章組。

-提供一個數據塊,如提交一個表單給一個數據處理過程。

-通過追加操作來擴展數據庫。

POST方法的實際功能是由服務器決定的,並且經常依賴於請求URI(Request-URI)。POST提交的實體是請求URI的從屬物,就好像一個文件從屬於一個目錄,一篇新聞文章從屬於一個新聞組,或者一條記錄從屬於一個數據庫。

POST方法執行的動作可能不會對請求URI所指的資源起作用。在這種情況下,200(成功)或者204(沒有內容)將是適合的響應狀態,這依賴於響應是否包含一個描述結果的實體。

如果資源被源服務器創建,響應應該是201(Created)並且包含一個實體,此實體描述了請求的狀態並且此實體引用了一個新資源和一個Location頭域(見14.30節)。

POST方法的響應是可緩存的。除非響應裏有Cache-Control或者Expires頭域指示其響應不可緩存。然而,303(見其他)響應能被利用去指導用戶代理(agent)去獲得可緩存的響應。

POST 請求必須遵循8.2節裏指明的消息傳輸需求。

參見15.1.3節關於安全性的考慮.

9.6 PUT

PUT方法請求服務器去把請求裏的實體存儲在請求URI(Request-URI)標識下。如果請求URI(Request-URI)指定的的資源已經在源服務器上存在,那麽此請求裏的實體應該被當作是源服務器此URI所指定資源實體的修改版本。如果請求URI(Request-URI)指定的資源不存在,並且此URI被用戶代理(user agent,譯註:用戶代理可認為是客戶瀏覽器)定義為一個新資源,那麽源服務器就應該根據請求裏的實體創建一個此URI所標識下的資源。如果一個新的資源被創建了,源服務器必須能向用戶代理(user agent) 發送201(已創建)響應。如果已存在的資源被改變了,那麽源服務器應該發送200(Ok)或者204(無內容)響應。如果資源不能根據請求URI創建或者改變,一個合適的錯誤響應應該給出以反應問題的性質。實體的接收者不能忽略任何它不理解的Content-*(如:Content-Range)頭域,並且必須返回501(沒有被實現)響應。

如果請求穿過一個緩存(cache),並且此請求URI(Request-URI)指示了一個或多個當前緩存的實體,那麽這些實體應該被看作是舊的。PUT方法的響應不應該被緩存。

POST方法和PUT方法請求最根本的區別是請求URI(Request-URI)的含義不同。POST請求裏的URI指示一個能處理請求實體的資源(譯註:此資源可能是一段程序,如jsp裏的servlet) 。此資源可能是一個數據接收過程,一個網關(gateway,譯註:網關和代理服務器的區別是:網關可以進行協議轉換,而代理服務器不能,只是起代理的作用,比如緩存服務器其實就是一個代理服務器),或者一個單獨接收註釋的實體。而PUT方法請求裏有一個實體一一用戶代理知道URI意指什麽,並且服務器不能把此請求應用於其他URI指定的資源。如果服務器期望請求被應用於一個不同的URI,那麽它必須發送301(永久移動了)響應;用戶代理可以自己決定是否重定向請求。

一個獨立的資源可能會被許多不同的URI指定。如:一篇文章可能會有一個URI指定當前版本,此URI區別於其文章其他特殊版本的URI。這種情況下,一個通用URI的PUT請求可能會導致其資源的其他URI被源服務器定義。

HTTP/1.1沒有定義PUT方法對源服務器的狀態影響。

PUT請求必須遵循8.2節中的消息傳輸要求。

除非特別指出,PUT方法請求裏的實體頭域應該被用於資源的創建或修改。

9.7 DELETE(刪除)

DELETE方法請求源服務器刪除請求URI指定的資源。此方法可能會在源服務器上被人為的幹涉(或其他方法)。客戶端不能保證此操作能被執行,即使源服務器返回成功狀態碼。然而,服務器不應該指明成功除非它打算刪除資源或把此資源移到一個不可訪問的位置。

如果響應裏包含描述成功的實體,響應應該是200(Ok);如果DELETE動作沒有通過,應該以202(已接受)響應;如果DELETE方法請求已經通過了,但響應不包含實體,那麽應該以204(無內容)響應。

如果請求穿過緩存,並且請求URI(Request-URI)指定一個或多個緩存當前實體,那麽這些緩存項應該被認為是舊的。DELETE方法的響應是不能被緩存的。

9.8 TRACE

TRACE方法被用於激發一個遠程的,應用層的請求消息回路(譯註:TRACE方法讓客戶端測試到服務器的網絡通路,回路的意思如發送一個請返回一個響應,這就是一個請求響應回路,)。最後的接收者或者是接收請求裏Max-Forwards頭域值為0源服務器或者是代理服務器或者是網關。TRACE請求不能包含一個實體。

TRACE方法允許客戶端知道請求鏈的另一端接收什麽,並且利用那些數據去測試或診斷。Via頭域值(見14.45)有特殊的用途,因為它可以作為請求鏈的跟蹤信息。利用Max-Forwards頭域允許客戶端限制請求鏈的長度去測試一串代理服務器是否在無限回路裏轉發消息。

如果請求是有效的,響應應該在響應實體主體裏包含整個請求消息,並且響應應該包含一個Content-Type頭域值為”message/http”的頭域。TRACE方法的響應不能不緩存。

9.9 CONNECT(連接)

HTTP1.1協議規範保留了CONNECT方法,此方法是為了能用於能動態切換到隧道的代理服務器(proxy,譯註:可以為代理,也可以是代理服務器)。


上邊的內容對HTTP Method 說的已經很詳細了,但冪等這個概念可能不太容易理解。下邊我們就著重介紹下:

在HTTP/1.1規範中冪等性的定義是:

Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.

從定義上看,HTTP方法的冪等性是指一次和多次請求某一個資源應該具有同樣的副作用。冪等性屬於語義範疇,正如編譯器只能幫助檢查語法錯誤一樣,HTTP規範也沒有辦法通過消息格式等語法手段來定義它,這可能是它不太受到重視的原因之一。但實際上,冪等性是分布式系統設計中十分重要的概念,而HTTP的分布式本質也決定了它在HTTP中具有重要地位。

為什麽需要冪等性呢?我們先從一個例子說起,假設有一個從賬戶取錢的遠程API(可以是HTTP的,也可以不是),我們暫時用類函數的方式記為:

bool withdraw(account_id, amount)

withdraw的語義是從account_id對應的賬戶中扣除amount數額的錢;如果扣除成功則返回true,賬戶余額減少amount;如果扣除失敗則返回false,賬戶余額不變。值得註意的是:和本地環境相比,我們不能輕易假設分布式環境的可靠性。一種典型的情況是withdraw請求已經被服務器端正確處理,但服務器端的返回結果由於網絡等原因被掉丟了,導致客戶端無法得知處理結果。如果是在網頁上,一些不恰當的設計可能會使用戶認為上一次操作失敗了,然後刷新頁面,這就導致了withdraw被調用兩次,賬戶也被多扣了一次錢。如圖1所示:

<center>


技術分享圖片

</center>

這個問題的解決方案一是采用分布式事務,通過引入支持分布式事務的中間件來保證withdraw功能的事務性。分布式事務的優點是對於調用者很簡單,復雜性都交給了中間件來管理。缺點則是一方面架構太重量級,容易被綁在特定的中間件上,不利於異構系統的集成;另一方面分布式事務雖然能保證事務的ACID性質,而但卻無法提供性能和可用性的保證。

另一種更輕量級的解決方案是冪等設計。我們可以通過一些技巧把withdraw變成冪等的,比如:

int create_ticket() 
bool idempotent_withdraw(ticket_id, account_id, amount)

create_ticket的語義是獲取一個服務器端生成的唯一的處理號ticket_id,它將用於標識後續的操作。idempotent_withdraw和withdraw的區別在於關聯了一個ticket_id,一個ticket_id表示的操作至多只會被處理一次,每次調用都將返回第一次調用時的處理結果。這樣,idempotent_withdraw就符合冪等性了,客戶端就可以放心地多次調用。

基於冪等性的解決方案中一個完整的取錢流程被分解成了兩個步驟:1.調用create_ticket()獲取ticket_id;2.調用idempotent_withdraw(ticket_id, account_id, amount)。雖然create_ticket不是冪等的,但在這種設計下,它對系統狀態的影響可以忽略,加上idempotent_withdraw是冪等的,所以任何一步由於網絡等原因失敗或超時,客戶端都可以重試,直到獲得結果。如圖2所示:

<center>


技術分享圖片

</center>

和分布式事務相比,冪等設計的優勢在於它的輕量級,容易適應異構環境,以及性能和可用性方面。在某些性能要求比較高的應用,冪等設計往往是唯一的選擇。

HTTP的冪等性

HTTP協議本身是一種面向資源的應用層協議,但對HTTP協議的使用實際上存在著兩種不同的方式:一種是RESTful的,它把HTTP當成應用層協議,比較忠實地遵守了HTTP協議的各種規定;另一種是SOA的,它並沒有完全把HTTP當成應用層協議,而是把HTTP協議作為了傳輸層協議,然後在HTTP之上建立了自己的應用層協議。本文所討論的HTTP冪等性主要針對RESTful風格的,不過正如上一節所看到的那樣,冪等性並不屬於特定的協議,它是分布式系統的一種特性;所以,不論是SOA還是RESTful的Web API設計都應該考慮冪等性。下面將介紹HTTP GET、DELETE、PUT、POST四種主要方法的語義和冪等性。

HTTP GET方法用於獲取資源,不應有副作用,所以是冪等的。比如:GET http://www.bank.com/account/123456,不會改變資源的狀態,不論調用一次還是N次都沒有副作用。請註意,這裏強調的是一次和N次具有相同的副作用,而不是每次GET的結果相同。GET http://www.news.com/latest-news這個HTTP請求可能會每次得到不同的結果,但它本身並沒有產生任何副作用,因而是滿足冪等性的。

HTTP DELETE方法用於刪除資源,有副作用,但它應該滿足冪等性。比如:DELETE http://www.forum.com/article/4231,調用一次和N次對系統產生的副作用是相同的,即刪掉id為4231的帖子;因此,調用者可以多次調用或刷新頁面而不必擔心引起錯誤。

比較容易混淆的是HTTP POST和PUT。POST和PUT的區別容易被簡單地誤認為“POST表示創建資源,PUT表示更新資源”;而實際上,二者均可用於創建資源,更為本質的差別是在冪等性方面。在HTTP規範中對POST和PUT是這樣定義的:

The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line ...... If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity which describes the status of the request and refers to the new resource, and a Location header.

The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.

POST所對應的URI並非創建的資源本身,而是資源的接收者。比如:POST http://www.forum.com/articles的語義是在http://www.forum.com/articles下創建一篇帖子,HTTP響應中應包含帖子的創建狀態以及帖子的URI。兩次相同的POST請求會在服務器端創建兩份資源,它們具有不同的URI;所以,POST方法不具備冪等性。而PUT所對應的URI是要創建或更新的資源本身。比如:PUT http://www.forum/articles/4231的語義是創建或更新ID為4231的帖子。對同一URI進行多次PUT的副作用和一次PUT是相同的;因此,PUT方法具有冪等性。

在介紹了幾種操作的語義和冪等性之後,我們來看看如何通過Web API的形式實現前面所提到的取款功能。很簡單,用POST /tickets來實現create_ticket;用PUT /accounts/account_id/ticket_id&amount=xxx來實現idempotent_withdraw。值得註意的是嚴格來講amount參數不應該作為URI的一部分,真正的URI應該是/accounts/account_id/ticket_id,而amount應該放在請求的body中。這種模式可以應用於很多場合,比如:論壇網站中防止意外的重復發帖。

上面簡單介紹了冪等性的概念,用冪等設計取代分布式事務的方法,以及HTTP主要方法的語義和冪等性特征。其實,如果要追根溯源,冪等性是數學中的一個概念,表達的是N次變換與1次變換的結果相同,有興趣的讀者可以從Wikipedia上進一步了解。

參考資料

  1. https://www.quora.com/What-is-the-history-of-HTTP-verbs-PUT-GET-POST-and-DELETE
  2. http://www.cnblogs.com/weidagang2046/archive/2011/06/04/idempotence.html
  3. http://www.360doc.com/content/15/1124/19/29350465_515532644.shtml
  4. https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
作者:老馬的春天
鏈接:https://www.jianshu.com/p/1a49a7b08ee0
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權並註明出處。

HTTP Method 詳細解讀(`GET` `HEAD` `POST` `OPTIONS` `PUT` `DELETE` `TRACE` `CONNECT`)--轉