HTTP 狀態碼詳解
最近看《REST in Practice》,發現 HTTP 如此之多的狀態碼都有各自的含義,要準確使用并不難,但現實當中很少人能夠做得到。大多數人熟悉的狀態碼就那幾個,平時也不會去閱讀 RFC 2616,結果反復使用的也就是那幾個狀態碼。其實很多 REST 中可能遇到的情況,在 HTTP 狀態碼中都已經有考慮到,不需要自己去發明新的狀態碼,也不需要在 header 或者 body 自定義錯誤信息。
在說狀態碼之前,首先建議大家還是先閱讀一下 RFC 2616 中的相關章節,看看已有的狀態碼描述都是什么。我相信有部分狀態碼是你看了描述也不知道用來干什么的,這時候就需要有更具體的例子來告訴你怎么用了。(我準備詳細說的是那些比較少人知道但又實際上應該被更廣泛使用的狀態碼。)
2xx
200 OK
所有人都知道 200 OK
是什么。這估計是最經常被濫用的狀態碼。很多人在應該使用其它 2xx
狀態碼時都選用了 200 OK
來表示。
201 Created
如果你在設計一個 REST API,或者一個 CRUD API,當你使用 POST
(或者 PUT
)成功創建一個新的資源后,服務器應該返回 201 Created
同時在 header 的 Location
字段給出剛剛創建好的這個資源的 URI。
例如說,如果你使用 POST
請求通過 \comments
URI 創建了一個新的評論,那么服務器應該返回 201 Created
,同時帶上形如 Location: \comments\1234
的字段表明新創建的評論的 URI。
202 Accepted
如果服務器在接受請求后,需要進行一個異步處理才能有結果,并且覺得不需要讓 TCP 連接保持到結果出來再返回,它可以返回 202 Accepted
,意思是請求已接受,但沒有立即可返回的結果。
204 No Content
在一個 REST API 或者 CRUD API 里面,當你使用 PUT
成功更新一個資源后,如果服務器完整接受了客戶端的更新,沒有拒絕也沒有額外更新,那實際上是不需要返回任何東西的,因為現在客戶端和服務器端已經擁有完全一致的狀態。在這種情況下,服務器可以返回 204 No Content
,同時 body 為空,客戶端就知道它的狀態已經跟服務器端同步了。
206 Partial Content
斷點續傳和多線程下載都是通過 206 Partial Content
實現的。
請求的 header 必須包含一個 Range
字段,表明自己請求第幾個字節到第幾個字節的內容。如果服務器支持的話,就返回 206 Partial Content
,然后使用 header 的 Content-Range
指明范圍,并在 body 內提供這個范圍內的數據。
3xx
301 Moved Permanently
永久性重定向。目標由 header 的 Location
字段給出,同時 body 中也應該有指向目標的鏈接。新請求使用的方法應該和原請求的一致。如果用戶使用 HEAD
和 GET
以外的方式發起原請求,客戶端在遇到 301 Moved Permanently
后應當詢問用戶是否對新的 URI 發起新請求。
302 Found
臨時性重定向。
這應該是瀏覽器實現最不符合標準的一個狀態碼了。理論上,除了臨時性這一點,302 Found
跟 301 Moved Permanently
應該是完全一樣的。然而實質上,很多瀏覽器在遇到 302 Found
后就會使用 GET
去請求新的 URI,而無論原請求使用的是何種方法。由于這種現象的普遍存在,使得這成為了一個與書面標準相違背的事實標準,新的客戶端在實現時很難選擇應該遵守哪一個標準,所以 RFC 2616 專門新增了 303 See Other
和 307 Temporary Redirect
兩個狀態碼來消除二義性。
303 See Other
臨時性重定向,且總是使用 GET
請求新的 URI。
304 Not Modified
如果客戶端發起了一個「條件 GET
」,同時資源確實沒被修改過,那么服務器端就應該返回 304 Not Modified
,同時 body 不包含任何內容。
所謂的「條件 GET
」,是指 GET
的 header 帶上了 If-Modified-Since
或 If-None-Match
字段。這兩個 header 就是「條件」,如果條件符合了 GET
就應該正常執行,否則就應該返回 304 Not Modified
,以便告訴客戶端它想要請求的資源在上一次請求之后沒有被更新過,客戶端可以繼續使用之前的版本。
307 Temporary Redirect
臨時性重定向,且總是使用原請求的方法來進行新請求。
4xx
400 Bad Request
服務器無法理解請求的格式,客戶端不應當嘗試再次使用相同的內容發起請求。
401 Unauthorized
請求未授權。如果請求 header 沒有 Authorization
字段,服務器端應該在返回 401 Unauthorized
的同時在 header 中用 WWW-Authorization
字段指出授權方式,以便客戶端帶上登錄信息重新發起請求。如果 Authorization
字段已經存在,則表明登錄信息不正確。
402 Payment Required
需要支付。這是一個在任何瀏覽器中都沒有被實現的狀態碼,僅預留將來使用。
百度曾經有一個部門印過一批背上寫著 402 Payment Require
的衣服,并且開玩笑說這批衣服最適合在互聯網企業員工討薪時穿。
403 Forbidden
禁止訪問。即使使用 Authorization
字段提供登錄信息也會得到相同的結果。
如果客戶端使用 HEAD
以外的方法請求,403 Forbidden
必須同時在 body 中返回禁止訪問的原因。如果原因不能夠公開,則應該使用 404 Not Found
。
404 Not Found
找不到如何與 URI 相匹配的資源。服務器無需指出資源是臨時性不存在還是永久性不存在,但如果服務器端知道該資源已經被永久性刪除則應該返回 410 Gone
。
404 Not Found
是服務器端在不愿意提供理由的情況下拒絕提供資源的最佳借口。
405 Method Not Allowed
請求的方法被拒絕。
如果你有一個 REST API 或 CRUD API 被設計為只讀,那么在遇到 PUT
、POST
或者 DELETE
方法時服務器端都應該返回 405 Method Not Allowed
,同時在 header 的 Allow
字段說明允許的方法(如 GET
和 HEAD
)。
409 Conflict
沖突,且需要用戶手工解決。
如果你使用 git(或者其他源代碼管理軟件),你已經知道「沖突」是什么了。409 Conflict
通常發生在 PUT
請求時,如果要更新的資源已經被其他人更新過了,你再更新就可能產生沖突。
410 Gone
如果服務器端將此資源標記為已被永久性刪除,則應該使用 410 Gone
而非 404 Not Found
,其用意在于告訴客戶端資源是被有意刪除的,而且刪除是永久性的,客戶端不應該再保留這個 URI 的鏈接。
舉例來說,你有一個 REST API 或 CRUD API 用于向用戶提供優惠信息。有一則優惠的 URI 是 /promotions/1234
,但由于優惠活動已經結束了,所以這一則優惠信息不再有效且應當被永久性刪除,那么這時候服務器端就應該讓該 URI 永遠返回 410 Gone
了。
412 Precondition Failed
條件判斷失敗,操作不會被執行。
在解釋 304 Not Modified
時提到了「條件 GET
」的概念,但「條件」本身也可以應用于非 GET
請求,這時候如果條件判斷失敗服務器端就應該返回 412 Precondition Failed
,同時拒絕執行客戶端請求的方法。
條件請求可以被看作是一種樂觀鎖。它不需要服務器端有任何邏輯判斷操作是否存在沖突,服務器端只要記錄資源的時間戳(或其它版本信息)即可。
5xx
500 Internal Server Error
最常見的服務器端錯誤。
503 Service Unavailable
服務器端暫時無法處理請求(可能是過載或維護)。
返回 503 Service Unavailable
的意思是當前的狀況是臨時性的,客戶端可以稍后重試。服務器端可以在返回時通過 header 的 Retry-After
字段告訴客戶端多久后可以重試。如果不提供這個字段的話,客戶端應當把 503 Service Unavailable
等同于 500 Internal Server Error
處理。
總結
在看完這篇文章后,你有發現經常用錯的狀態碼嗎?如果有的話,將來在設計 REST API 或 CRUD API 時就應該改過來。由于狀態碼和 header 字段數目眾多,所以我建議一般用戶盡可能復用主流的 REST 框架或 CRUD 框架,而不要自己重新實現一遍。
轉自:http://www.cnblogs.com/cathsfz/archive/2012/06/19/2553431.html