11. RESTful API 設計最佳實踐
API 設計規範
URI 的設計
在RESTful架構中,每個網址代表一種資源(resource),一般是名詞且表示方式為複數。
URI 使用小寫,如果資源由多個單片語成,使用
-
連線。對於資源的具體操作型別,由HTTP動詞表示。
常用的HTTP動詞有下面五個(括號裡是對應的SQL命令)。
GET(SELECT):從伺服器取出資源(一項或多項)。
POST(CREATE):在伺服器新建一個資源。
PUT(UPDATE):在伺服器更新資源。
DELETE(DELETE):從伺服器刪除資源。
下面是一些例子。
- GET /zoos:列出所有動物園
- POST /zoos:新建一個動物園
- GET /zoos/ID:獲取某個指定動物園的資訊
- PUT /zoos/ID:更新某個指定動物園的資訊
- DELETE /zoos/ID:刪除某個動物園
- GET /zoos/ID/animals:列出某個指定動物園的所有動物
- DELETE /zoos/ID/animals/ID:刪除某個指定動物園的指定動物
過濾、排序和搜尋等資訊
過濾
對每一個欄位使用一個唯一查詢引數,就可以實現過濾。
例如:
GET /tickets?state=open // 獲取狀態為開放的票
排序
採用泛型引數(sort
)來描述排序的規則,排序引數採取逗號分隔的欄位列表的形式,每一個欄位前都可能有一個負號來表示按降序排序。
例如:
GET /tickets?sort=-priority // 獲取票據列表,按優先順序欄位降序排序
GET /tickets?sort=-priority,created_at // 獲取票據列表,按“priority”欄位降序排序。在一個特定的優先順序內,較早的票排在前面。
搜尋
當我們需要全文搜尋或者需要搜尋多個欄位時,我們可以採取泛型引數(q
)的形式來表示。
GET /tickets?q=return // 查詢印有 "return" 字樣的票
分頁引數
使用 page
和 per_page
來表示當前處於第幾頁及每頁的記錄數。
GET /tickets?page=1&per_page=10
響應和錯誤處理
響應
統一採用 json
形式。
json 中的欄位名採用 snake_case 形式,如 err_code
而非 errCode
。
結構和錯誤處理
由於在複雜的系統中 http 狀態碼並不能覆蓋所有的錯誤處理。為此我們採用所有響應的狀態碼都為 200
,只是在返回結果中表明狀態和錯誤資訊,結構如下:
{
"status": "success", // success or fail, 用來表明請求狀態:成功 或 出錯
"err_code": "",
"err_msg": "",
"data": {
...
}
}
// 如果 status 為 success,則可讀取 data 裡面的內容作後續處理。
// 如果 status 為 fail ,則讀取 err_code 和 err_msg。
這樣設計的好處在於既不用關心 http 狀態碼,也不用對成功或失敗來處理不同的JSON結構。
版本控制(delete)
給 API 標識版本有利於向後相容和軟體的平滑升級。
關於 API 的版本是應該包含在 URL 中還是請求頭中的討論,沒有明確的答案,誰都說服不了誰。
此處,我們採取放在 URL 中的方式,比較簡單明瞭。
例如:
https://api.example.com/v1/candidates
認證
由於 Restful API 是無狀態的,當我們需要進行許可權認證時,可在請求頭裡加上請求憑證(access_token),可採用 OAuth 2.0 框架。
快取
HTTP提供了自帶的快取框架。你需要做的是在返回的時候加入一些返回頭資訊,在接受輸入的時候加入輸入驗證。基本兩種方法:
- ETag:當生成請求的時候,在HTTP頭裡面加入ETag,其中包含請求的校驗和和雜湊值,這個值和在輸入變化的時候也應該變化。如果輸入的HTTP請求包含IF-NONE-MATCH頭以及一個ETag值,那麼API應該返回304 not modified狀態碼,而不是常規的輸出結果。
- Last-Modified:和etag一樣,只是多了一個時間戳。返回頭裡的Last-Modified:包含了 RFC 1123 時間戳,它和IF-MODIFIED-SINCE一致。HTTP規範裡面有三種date格式,伺服器應該都能處理。
未完待續…
由於 api 規範的定製不是一個一蹴而就的事情,可能會隨著遇到的問題有所增加或調整,後面也會持續的完善和更新。