332
技術社區[雲棲]
PUT 還是 POST ?
創建,更新與HTTP冪等性
構建基於REST API的開發者對於何時使用HTTP PUT與POST有很大的誤解與困惑。有些人認為POST 應用於創建資源,而PUT則用於更新資源。其他人則認為PUT用於創建而POST用於更改資源。這兩種說法都不太確切。
通常,開發者將每個HTTP方法與CRUP操作一一對應。
CRUD HTTP
Create POST
Read GET
Update PUT
Delete DELETE
的確是這樣,而且GET與DELETE對應的操作是很明確的,但論及與create和update對應的HTTP方法時要取決於冪等性。
狀態統一性
狀態統一性在HTTP規範中是一個很重要的概念。它規定對於執行多次相同的HTTP請求,處於服務端的資源的狀態是相同的。GET,HEAD, PUT與DELETE都具有這種特性,但POST沒有。
為便於說明狀態統一性,我們會使用一個Account集合("/accounts"),並且為了簡潔我們假設每個account資源都有三個屬性:givenName, surname,和status。
假設你使用HTTP PUT方法提交了一個update請求。在請求體中,你設置了givenName和surname的值分別為"John"和"Smith"。接著你又提交了另一個HTTP PUT的請求,這次你將givenName的值設置為"Johnny"。它是冪等的麼?不。為什麼呢?因為在這兩次請求的間隙,其他請求可能已經改變了account資源的服務端狀態。例如,在這兩次請求之間,status的值可能已經變成了“blocked"。我們示範的請求在重複提交時不能保證在服務端的account資源的狀態是相同的。
請求:
HTTP/1.1 PUT /accounts/abcdef1234
{
“givenName”: “John”,
“surname”: “Smith”
}
這兩次請求後可能的account狀態。 (受其他請求的副作用影響):
{
“givenName”: “John”,
“surname”: “Smith”,
“status”: “enabled”
}
或
{
“givenName”: “John”,
“surname”: “Smith”,
"status”: “disabled”
}
引用Dino Chiesa所述, “PUT 意為提交一個資源——用一個不同的事物完全替代給定的URL下的所有可訪問資源。” 要使用PUT請求,你必須發送所有可訪問屬性/值,而不僅僅是你想要改變的那些。 如果我們在發送givenName和surname時加上狀態值“disabled",則本次調用是冪等的並且消除了副作用。冪等性是HTTP規範的一項基本屬性,並且必須確保web的互操作性與規模。
最後,我們應該指出HTTP狀態統一性隻能應用於服務端狀態——非客戶端。例如,某一客戶端成功地發送了一個服務端狀態統一請求,並立即再次發送這個請求但返回了一個錯誤(例如可能是由於違反了服務端某種約束),對於 HTTP這仍是‘合法’的。因為這個請求沒有對服務端資源狀態產生影響,HTTP冪等性沒有破壞。
HTTP POST vs HTTP PUT
既然已經清楚了狀態統一性,在執行創建和更新操作時你應該使用哪個方法呢?以下是用於合理使用這些方法的快速參考。
創建
在不知道資源標識符時你應該使用POST來創建資源。使用POST創建資源時,返回“201 Created"狀態和新建資源的位置是很好的實踐,因為新建資源的位置在提交時是未知的。這可以使客戶端稍後訪問新創建的資源如果需要的話。
HTTP/1.1 POST /accounts
{
…
}
響應:
201 Created
Location: https://api.stormpath.com/accounts/abcdef1234
當你允許客戶端指定新建資源的資源標識符時要使用PUT。但要記住,因為PUT是冪等的,你必須要發送所有可能的值。
HTTP/1.1 PUT /accounts/abcdef1234
{
“givenName”: “John”,
“surname”: “Smith”,
“status”: “enabled”
}
更新
你可以使用POST更新全部或一部分值。
HTTP/1.1 POST /accounts/abcdef1234
{
“status”: “disabled”
}
Response 200 OK
如果你想用PUT更新某一資源,則必須要更新資源的全部屬性。你必須要在PUT請求中發送所有屬性值以保證冪等性。
HTTP/1.1 PUT /accounts/abcdef1234
{ //FULL RESOURCE UPDATE
“givenName”: “J”,
“surname”: “Smith”,
“status”: “Enabled”
}
你也可以使用POST發送所有值,這樣服務端狀態與處理PUT請求的結果是一樣的——這不是HTTP規範所必需的。注意冪等性與HTTP緩存服務器的緩存有較強的關聯,並且POST請求通常是非緩存的。如果你對緩存的副作用感冒的話,你可以使用POST來執行全部或部分更新。
POST是目前惟一的狀態不統一的方法。HTTP規範對它的定義也很寬泛,並且大體上將它定義為了一個“服務端處理指令”。這就意味著在POST請求中做任何處理都是“安全”的。
最後我們要注意HTTP規範中還沒有完成的另一個稱為PATCH的方法。PATCH意為用於執行部分更新時的POST的替代品。然而,因為POST已經可以處理部分更新,HTTP協會似乎沒有急著批準和完成PATCH的必要了。但如果批準了,PATCH將會加入POST作為另一個狀態不統一的HTTP方法。
最後更新:2017-07-05 17:02:27