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