1.通過請求類型區分CRUD
方法 | 操作 | 場景 |
GET | 讀 | /cars /cars/711 |
POST | 創建 | /cars |
PUT | 修改 | /cars/711 /cars |
PATCH | 部分修改 | /cars/711 |
DELETE | 刪除 | /cars/711 |
2.資源采用名詞,且使用復數
GET /tickets - 獲取 tickets 列表
GET /tickets/12 - 獲取一個單獨的 ticket
POST /tickets - 創建一個新的 ticket
PUT /tickets/12 - 更新 ticket #12
PATCH /tickets/12 - 部分更新 ticket #12
DELETE /tickets/12 - 刪除 ticket #12
3.Get方法和查詢參數不應該涉及狀態改變
不要使用GET 進行狀態改變,
GET /users/711?activate
GET /users/711/activate
4.使用子資源表達關系
如果一個資源與另外一個資源有關系,使用子資源:
GET /tickets/12/messages - 獲取ticket #12下的消息列表 GET /tickets/12/messages/5 - 獲取ticket #12下的編號為5的消息 POST /tickets/12/messages - 為ticket #12創建一個新消息 PUT /tickets/12/messages/5 - 更新ticket #12下的編號為5的消息 PATCH /tickets/12/messages/5 - 部分更新ticket #12下的編號為5的消息 DELETE /tickets/12/messages/5 - 刪除ticket #12下的編號為5的消息
5.使用Http頭聲明序列化格式
在客戶端和服務端,雙方都要知道通訊的格式,格式在HTTP-Header中指定
Content-Type 定義請求格式
Accept 定義系列可接受的響應格式
6.過濾,排序,搜索設計
最好是盡量保持基本資源URL的簡潔性。 復雜結果過濾器、排序需求和高級搜索 (當限定在單一類型的資源時) ,都能夠作為在基本URL之上的查詢參數來輕松實現。下面讓我們更詳細的看一下:
1)過濾: 對每一個字段使用一個唯一查詢參數,就可以實現過濾。 例如,當通過“/tickets”終端來請求一個票據列表時,你可能想要限定只要那些在售的票。這可以通過一個像
GET /tickets?state=open
這樣的請求來實現。這里“state”是一個實現了過濾功能的查詢參數。
2)排序: 跟過濾類似, 一個泛型參數排序可以被用來描述排序的規則. 為適應復雜排序需求,讓排序參數采取逗號分隔的字段列表的形式,每一個字段前都可能有一個負號來表示按降序排序。我們看幾個例子:
GET /tickets?sort=-priority - 獲取票據列表,按優先級字段降序排序 GET /tickets?sort=-priority,created_at - 獲取票據列表,按“priority”字段降序排序。在一個特定的優先級內,較早的票排在前面
3)搜索: 有時基本的過濾不能滿足需求,這時你就需要全文檢索的力量。或許你已經在使用 ElasticSearch 或者其它基於 Lucene 的搜索技術。當全文檢索被用作獲取某種特定資源的資源實例的機制時, 它可以被暴露在API中,作為資源終端的查詢參數,我們叫它“q”。搜索類查詢應當被直接交給搜索引擎,並且API的產出物應當具有同樣的格式,以一個普通列表作為結果。
把這些組合在一起,我們可以創建以下一些查詢:
GET /tickets?sort=-updated_at - 獲取最近更新的票 GET /tickets?state=closed&sort=-updated_at - 獲取最近更新並且狀態為關閉的票。 GET /tickets?q=return&state=open&sort=-priority,created_at - 獲取優先級最高、最先創建的、狀態為開放的票,並且票上有 'return' 字樣。
4)一般查詢定義方式
為了使普通用戶的API使用體驗更加愉快, 考慮把條件集合包裝進容易訪問的RESTful 路徑中。比如上面的,最近關閉的票的查詢可以被包裝成
GET /tickets/recently_closed
5)限制查詢返回字段
API的使用者並不總是需要一個資源的完整表示。選擇返回字段的功能由來已久,它使得API使用者能夠最小化網絡阻塞,並加速他們對API的調用。
使用一個字段查詢參數,它包含一個用逗號隔開的字段列表。例如,下列請求獲得的信息將剛剛足夠展示一個在售票的有序列表:
GET /tickets?fields=id,subject,customer_name,updated_at&state=open&sort=-updated_at
6)Paging分頁
使用 limit 和offset.實現分頁,缺省limit=20 和offset=0;
GET /cars?offset=10&limit=5
為了將總數發給客戶端,使用訂制的HTTP頭: X-Total-Count.
鏈接到下一頁或上一頁可以在HTTP頭的link規定,遵循Link規定:
Link: <https://blog.mwaysolutions.com/sample/api/v1/cars?offset=15&limit=5>; rel="next", <https://blog.mwaysolutions.com/sample/api/v1/cars?offset=50&limit=3>; rel="last", <https://blog.mwaysolutions.com/sample/api/v1/cars?offset=0&limit=5>; rel="first", <https://blog.mwaysolutions.com/sample/api/v1/cars?offset=5&limit=5>; rel="prev",
7.更新和創建應該返回一個資源描述
一個 PUT, POST 或者 PATCH 調用可能會對指定資源的某些字段造成更改,而這些字段本不在提供的參數之列 (例如: created_at 或 updated_at 這兩個時間戳)。 為了防止API使用者為了獲取更新后的資源而再次調用該API,應當使API把更新(或創建)后的資源作為response的一部分來返回。
以一個產生創建活動的 POST 操作為例, 使用一個 HTTP 201 狀態代碼 然后包含一個 Location header 來指向新生資源的URL。
8.只返回JSON
9.字段名稱書寫格式統一
選定一種方式:snake_case vs camelCase
10.缺省情況下采用格式化形式和支持gzip
一個提供空白符壓縮輸出的API,從瀏覽器中查看結果並不美觀。雖然一些有序的查詢參數(如 ?pretty=true )可以提供來使漂亮打印生效,一個默認情況下能進行漂亮打印的API更為平易近人。額外數據傳輸的成本是微不足道的,尤其是當你比較不執行gzip壓縮的成本。
考慮一些用例:假設分析一個API消費者正在調試並且有自己的代碼來打印出從API收到的數據——默認情況下這應是可讀的。或者,如果消費者抓住他們的代碼生成的URL,並直接從瀏覽器訪問它——默認情況下這應是可讀的。這些都是小事情。做好小事情會使一個API能被更愉快地使用
11.錯誤設計
就像一個HTML錯誤頁面給訪問者展示了有用的錯誤信息一樣,一個API應當以一種已知的可使用的格式來提供有用的錯誤信息。 錯誤的表示形式應當和其它任何資源沒有區別,只是有一套自己的字段。
API應當總是返回有意義的HTTP狀態代碼。API錯誤通常被分成兩種類型: 代表客戶端問題的400系列狀態碼和代表服務器問題的500系列狀態碼。最簡情況下,API應當把便於使用的JSON格式作為400系列錯誤的標准化表示。如果可能(意思是,如果負載均衡和反向代理能創建自定義的錯誤實體), 這也適用於500系列錯誤代碼。
一個JSON格式的錯誤信息體應當為開發者提供幾樣東西 - 一個有用的錯誤信息,一個唯一的錯誤代碼 (能夠用來在文檔中查詢詳細的錯誤信息) 和可能的詳細描述。這樣一個JSON格式的輸出可能會像下面這樣:
{ "code" : 1234, "message" : "Something bad happened :(", "description" : "More details about the error here" }
對PUT, PATCH和POST請求進行錯誤驗證將需要一個字段分解。下面可能是最好的模式:使用一個固定的頂層錯誤代碼來驗證錯誤,並在額外的字段中提供詳細錯誤信息,就像這樣:
{ "code" : 1024, "message" : "Validation Failed", "errors" : [ { "code" : 5432, "field" : "first_name", "message" : "First name cannot have fancy characters" }, { "code" : 5622, "field" : "password", "message" : "Password cannot be blank" } ] }
12.HTTP狀態碼
HTTP定義了一套可以從API返回的有意義的狀態代碼。 這些代碼能夠用來幫助API使用者對不同的響應做出相應處理。我已經把你必然會用到的那些列成了一個簡短的清單:
- 200 OK (成功) - 對一次成功的GET, PUT, PATCH 或 DELETE的響應。也能夠用於一次未產生創建活動的POST
- 201 Created (已創建) - 對一次導致創建活動的POST的響應。 同時結合使用一個位置頭信息指向新資源的位置- Response to a POST that results in a creation. Should be combined with a Location header pointing to the location of the new resource
- 204 No Content (沒有內容) - 對一次沒有返回主體信息(像一次DELETE請求)的請求的響應
- 304 Not Modified (未修改) - 當使用HTTP緩存頭信息時使用304
- 400 Bad Request (錯誤的請求) - 請求是畸形的, 比如無法解析請求體
- 401 Unauthorized (未授權) - 當沒有提供或提供了無效認證細節時。如果從瀏覽器使用API,也可以用來觸發彈出一次認證請求
- 403 Forbidden (禁止訪問) - 當認證成功但是認證用戶無權訪問該資源時
- 404 Not Found (未找到) - 當一個不存在的資源被請求時
- 405 Method Not Allowed (方法被禁止) - 當一個對認證用戶禁止的HTTP方法被請求時
- 410 Gone (已刪除) - 表示資源在終端不再可用。當訪問老版本API時,作為一個通用響應很有用
- 415 Unsupported Media Type (不支持的媒體類型) - 如果請求中包含了不正確的內容類型
- 422 Unprocessable Entity (無法處理的實體) - 出現驗證錯誤時使用
- 429 Too Many Requests (請求過多) - 當請求由於訪問速率限制而被拒絕時
----------------------------------------------------------------------
參考資料:
1、《Best Practices for Designing a Pragmatic RESTful API》
2、《RESTful API 設計最佳實踐》(對上文的譯文)
3、《Principles of good RESTful API Design》