上週五,我在作一個 HTTP 接口的遷移。恰好跟同事進行了一場關於 HTTP 接口的狀態返回的討論。很巧合地是,咱們的觀點互不相同,在他看來:segmentfault
全部業務返回的響應狀態碼都應該是200,包含業務異常的響應狀態碼也是200,錯誤的消息放在 Body 裏面描述;未到達業務層的請求,例如:5XX, 403 這樣的錯誤應該算做是 「鏈接層」 的錯誤。設計
這樣作的優勢是:code
對於 5XX, 403 這樣的錯誤能夠概括爲 「鏈接」 錯誤。能夠統一處理?接口
客戶端只需在接受到 200 的狀態碼時才作業務處理。資源
可是在我看來,這有一個很大的缺點:get
對於業務異常沒法在第一層的 status code 裏面發現,須要進一步解析 body, 響應的 body 有 2 個 Schema, 即 error message 和業務響應實體的 schema。這樣作的廠商其實不少,例如: LeanColud 的獲取數據的接口。class
GET /1.1/classes/<className>/<objectId>
在使用這個接口獲取一個不存在的 objectId
時,響應的格式是:擴展
Response Status Code: 200 Response Body: {}
這個接口把 className
與 objectId
都作成了 path parameter, 一般地,咱們不該該用 404 來描述一個 URL 不存在的狀況嗎?object
若是我要使用這個接口,我在客戶端接收到響應以後大概要作如下的處理:接口設計
判斷 http status code 是不是 200。
解析 http body 是不是一個 error message。
解析 http body 爲業務實體。
判斷解析後的業務實體是不是"空"。
因此,個人觀點恰好相反:全部響應的狀態要充分利用 http status code 來描述響應的異常狀況, 而且配合 http body 來描述更詳細的 error message。
我同事他認爲這樣作的缺點是:
http status code 是很是有限的,且不能擴展。
那麼咱們該怎麼處理這種 http status code 表達能力不足的狀況呢? 答案是: 使用 http status code 來表明某一個類型的異常,同時用 http response body 來描述更詳細的異常消息。 例如:
Response Status Code: 400 Response Body: { "code": "missing_parameter_name", "message": "請求中必須包含參數 name" } Response Status Code: 400 Response Body: { "code": "id_not_matched", "message": "id 格式應該爲: \w+" }
上面的例子中咱們使用了 400 來描述了一個 Bad Request
, 而且將更加詳細的業務異常表達出來了! 來看看我如何改造 LeanCloud 的接口返回:
Response Status Code: 200 Response Body: { "objectId": "xxx", "name": "這是一個業務實體" } Response Status Code: 404 Response Body: { "error": "resource_not_found", "message": "沒法在 className 下找打 objectId 資源" }
總結一下我這種設計方式的優勢:
能夠根據 http status code 判斷響應是否正常。
無需將一個 response body 適配多個 schema 解析。
客戶端友好,無需過多包裝 response body,在正常返回的狀況下數據能夠直接使用,無需拆箱。
接口設計能夠變幻無窮,能夠說沒有哪種方案是完美的,咱們在設計的接口的時候也要儘量地站在使用者的角度考慮下面的原則:
客戶端友好,響應的層次儘量簡潔。
數據不要過渡封裝,最好能作到即拿即用。
接口名稱簡潔明瞭。
對於 HTTP 接口的更多實踐經驗能夠查看個人文章 RESTful Best Practices