REST API 設計規範
在項目中往往會需要確定一個好的API風格,到底有哪些風格可以參考,API Style 的細節要點有哪些呢?
Http API Style 有哪些?
- SOAP:tend to be centered around operations that are usually use-case specific and specialized.
- REST:centered around business (data) entities exposed as resources that are identified via URIs and can be manipulated via standardized CRUD-like methods using different representations, and hypermedia
- GraphQL:a query language for APIs and a runtime for fulfilling those queries with your existing data
SOAP 風格(嚴格來說,算不上風格)最早於1998年由微軟提出;REST 風格於2000年 由 Roy Thomas Fielding 論文中提出;GraphQL 於2015年由 Facebook 提出;
- SOAP vs REST
如果要輕鬆、快速地完成API設計,SOAP 風格的API就足夠了。畢竟REST有時很難做到,尤其是在一開始。但隨着時間的推移,使用RESET 風格的API,服務器端的演進變得更容易,客戶機對變化的適應能力也更強。
-
REST vs GraphQL
REST 限於其歷史背景,對於
查詢
操作一些細節並沒有太多描述,隨着互聯網的發展,查詢的複雜度越來越高,而 GraphQL 是一個很好的補充。
在業界有將近70%的API是REST-like的風格,其中當然就包括谷歌、微軟等行業巨頭,REST 差不多已經成爲了事實上的標準,瞭解、用好 REST 十分必要。
REST 是什麼?
REST,全稱是 Resource Representational State Transfer:通俗來講就是:資源在網絡中以某種表現形式進行狀態轉移。分解開來:
- Resource:資源,即數據(前面說過網絡的核心)。比如 newsfeed,friends等;
- Representational:某種表現形式,比如用JSON,XML,JPEG等;
- State Transfer:狀態變化。通過HTTP動詞實現。
資源模型
資源是具有類型、數據、與其他資源關係、以及一組對其進行操作的方法的對象。
Richardson成熟度模型
Level 0
不使用任何URI,HTTP方法和HATEOAS功能。
該模型的出發點是使用HTTP作爲遠程交互的傳輸系統,但不使用Web的任何機制。基本上就是使用HTTP作爲你遠程交互機中的隧道機制,通常基於“遠程過程調用“(RPC, Remote Procedure Invocation )。
Level 1 - Resources
使用URI、HTTP方法、HATEOAS中的 URI 。
邁向REST的第一步就是引入資源的概念。接下來,我們所要討論的是各個資源,而不是將所有請求發送到單一的服務端點。每個資源都由唯一的URI單獨標識
Level 2 - HTTP Verbs
使用URI、HTTP方法、HATEOAS中 的URI和HTTP 。
支持每個公開資源上的幾個HTTP謂詞 - 創建,讀取,更新和刪除(CRUD)服務。通常代表業務實體的資源狀態可以通過網絡進行操作。
Level 3 - Hypermedia Controls
使用所有三個,即URI,HTTP和HATEOAS。
超媒體控制的關鍵在於它告訴我們下一步我們可以做什麼,以及操作所需資源的URI。與我們必須提前知道在哪裏創建預約請求不同(Level2中),在響應中的 HATEOAS 告訴了我們下一步該如何做,以完成應用程序狀態轉換。
關鍵問題
REST 本身不是標準,只是一種風格。因此只要遵從該風格,都是OK的。然而,除此之外我們逃不開使用中遇到的很多問題,最典型的問題,如下:
- Error Handling
- Sorting
- Pagination
- versioning
- filtering
- Long running
- Sub-collection
- Action(i.e. Batch Operation)
Error handling
詳見:跨服務錯誤處理
Sorting
如果 API 方法允許客戶端指定列表結果的排序順序,則請求消息 應該 包含一個字段:
string order_by = ...;
說明:語法中的冗餘空格字符是無關緊要的。 "foo,bar desc"
和 " foo , bar desc "
是等效的。
字符串值 應該 遵循 SQL 語法:逗號分隔的字段列表。例如: "foo,bar"
。默認排序順序爲升序。要將字段指定爲降序, 應該 將後綴 " desc"
附加到字段名稱中。例如: "foo desc,bar"
。
Pagination
- 可列表集合 應該 支持分頁,即使結果通常很小。
說明:如果某個 API 從一開始就不支持分頁,稍後再支持它就比較麻煩,因爲添加分頁會破壞 API 的行爲。 不知道 API 正在使用分頁的客戶端可能會錯誤地認爲他們收到了完整的結果,而實際上只收到了第一頁。
-
翻頁方式
後臺存儲 Request Response 搜索引擎 {
“page_num”: 1, // 頁碼從 1 開始
“page_size”: 10
}{
“code”: 0,
“msg”: “”,
“data”: {
“total_cnt”: 100,
“items”: []
}
}數據庫 {
“last_id”: 1, // 第一頁,默認傳0
“page_size”: 10
}{
“code”: 0,
“msg”: “”,
“data”: {
“items”: [],
“next_id”: 10 // 下一頁放到last_id的值
}
}
Versioning
業界方案
-
無版本控制
這是最簡單的方法,它對於一些內部 API 來說可能是可以接受的。 重大變化可以表示爲新資源或新鏈接。 向現有資源添加內容可能不會帶來重大更改,因爲不希望看到此內容的客戶端應用程序將忽略它。
https://adventure-works.com/customers/3
-
URI 版本控制
每次修改 Web API 或更改資源的架構時,向每個資源的 URI 添加版本號。 以前存在的 URI 應像以前一樣繼續運行,並返回符合原始架構的資源。
https://adventure-works.com/v2/customers/3
-
查詢字符串版本控制
不是提供多個 URI,而是可以通過在追加到 HTTP 請求後面的查詢字符串中使用參數來指定資源的版本,例如
https://adventure-works.com/customers/3?version=2
注意:某些較舊的 Web 瀏覽器和 Web 代理不會緩存在 URI 中包含查詢字符串的請求的響應。 這可能會降低使用 Web API 並在此類 Web 瀏覽器中運行的 Web 應用程序的性能。
-
標頭版本控制
不是追加版本號作爲查詢字符串參數,而是可以實現指示資源的版本的自定義標頭。 此方法需要客戶端應用程序將相應標頭添加到所有請求,雖然如果省略了版本標頭,處理客戶端請求的代碼可以使用默認值(版本 1)。 下面的示例使用了名爲 Custom-Header 的自定義標頭**。 此標頭的值指示 Web API 的版本。
GET https://adventure-works.com/customers/3 HTTP/1.1 Custom-Header: api-version=1
-
無版本控制:在更改RESTful API時,請以兼容的方式進行更改,並避免生成其他API版本。
說明:多個版本會使理解、測試、維護、發展、操作和發佈我們的系統變得非常複雜。
在更改RESTful api時,請以兼容的方式進行更改,並避免生成其他API版本。多個版本可能會顯著地複雜化查看、測試、維護、發展、運營和發佈系統( 補充閱讀 )。如果無法以兼容的方式更改API,請使用這三種方式:
- 在舊資源變體的基礎上創建新資源(變量)
- 創建一個新的服務端點-即一個具有新API的新應用程序(使用新域名)
- 在微服務中創建一個新的API版本,該版本支持與舊API同時支持
Filtering
在參數過濾時通常會爲參數值定義數據格式。爲了在所有 API 中提供一致的開發者體驗並減少學習曲線,API 設計人員 必須 使用以下 擴展巴科斯範式 (Extended Backus-Naur Form,簡寫爲“EBNF”)語法的變體來定義這樣的語法:
Production = name "=" [ Expression ] ";" ; Expression = Alternative { "|" Alternative } ; Alternative = Term { Term } ; Term = name | TOKEN | Group | Option | Repetition ; Group = "(" Expression ")" ; Option = "[" Expression "]" ; Repetition = "{" Expression "}" ;
注意: TOKEN
表示在語法之外定義的終端符號。
Example
GET /zoos?id=1001,1002,1003
Long running
有時,POST、PUT、PATCH 或 DELETE 操作可能需要一段時間才能完成。如果需要等待該操作完成後才能向客戶端發送響應,可能會造成不可接受的延遲。在這種情況下,請考慮將該操作設置爲異步操作。返回 HTTP 狀態代碼 202(已接受),指示該請求已接受進行處理,但尚未完成。
應公開一個可返回異步請求狀態的終結點,使客戶端能夠通過輪詢狀態終結點來監視狀態。在 202 響應的 Location 標頭中包含狀態終結點的 URI。例如:
HTTP/1.1 202 Accepted Location: /api/status/12345
如果客戶端向此終結點發送 GET 請求,響應中應包含該請求的當前狀態。(可選)響應中還可以包含預計完成時間,或者用於取消操作的鏈接
HTTP/1.1 200 OK Content-Type: application/json { "status":"In progress", "link": { "rel":"cancel", "method":"delete", "href":"/api/status/12345" } }
如果異步操作創建了新資源,則該操作完成後,狀態終結點應返回狀態代碼 303(查看其他)。在 303 響應中,包含一個 Location 標頭用於提供新資源的 URI:
HTTP/1.1 303 See Other Location: /api/orders/12345
有關詳細信息,請參閱 異步請求-回覆模式 。
Sub-collection
有時,API 需要讓客戶跨子集執行 List/Search
操作。例如,“API 圖書館”有一組書架,每個書架都有一系列書籍,而客戶希望在所有書架上搜索某一本書。在這種情況下,建議在子集合上使用標準 List
,併爲父集合指定通配符集合 ID "-"
。對於“API 圖書館”示例,我們可以使用以下 REST API 請求:
GET https://library.googleapis.com/v1/shelves/-/books/{id}
注意:選擇 "-"
而不是 "*"
的原因是爲了避免需要進行 URL 轉義。
Action
常用的HTTP動詞有下面五個(括號裏是對應的SQL命令)。
- GET(SELECT):從服務器取出資源(一項或多項)。
- POST(CREATE):在服務器新建一個資源。
- PUT(UPDATE):在服務器更新資源(客戶端提供改變的屬性)
- DELETE(DELETE):從服務器刪除資源。
對於非標準的操作,以上動詞無法無法滿足需求,可以在資源上使用“操作”子集合。 動作基本上類似於RPC的消息,用於對資源執行特定操作。 “動作”子集合可以看作是一個命令隊列,可以將新的動作發佈到該命令隊列中,然後由API執行。定義標準動詞如下:
- batch:批量操作
- search:搜索操作
GET /zoos:列出所有動物園 POST /zoos:新建一個動物園 GET /zoos/ID:獲取某個指定動物園的信息 PUT /zoos/ID:更新某個指定動物園的信息(提供該動物園的全部信息) PATCH /zoos/ID:更新某個指定動物園的信息(提供該動物園的部分信息) DELETE /zoos/ID:刪除某個動物園 GET /zoos/ID/animals:列出某個指定動物園的所有動物 DELETE /zoos/ID/animals/ID:刪除某個指定動物園的指定動物 GET /zoos/-/action/batch 批量查詢 POST /zoos/-/action/batch 批量更新 POST /zoos/-/action/search 搜索
其他
-
對
null
和不存在的屬性使用相同的語義required nullable {} {“example”:null} true
true
✗ No ✔ Yes false
true
✔ Yes ✔ Yes true
false
✗ No ✗ No false
false
✔ Yes ✗ No -
路徑使用 中劃線
-
代替 下劃線_
;在搜索引擎中,把中劃線當做空格處理,而下劃線是被忽略的。使用中劃線是對搜索引擎友好的寫法
Example:
/shipment-orders/{shipment-order-id}
-
範圍
表示範圍的字段 應該 使用半開區間和命名慣例
[start_xxx, end_xxx)
,例如[start_key, end_key)
或[start_time, end_time)
。通常 C ++ STL 庫和 Java 標準庫會使用半開區間語義。API 應該 避免使用其他表示範圍的方式,例如(index, count)
或[first, last]
。
總結
完成以上這些,也僅僅是達到REST Level 2,由於Level 3 對於API風格影響不大,暫不涉及。對 HATEOAS 感興趣,可以參考 Github v3 版本的API。
參考鏈接
- Google Cloud API guide
- Microsoft API design best practices
- Zalando RESTful API and Event Scheme Guidelines
- OpenAPI-Specification
- Consumer-Centric API Design v0.4.0.pdf
- s3 rest api and post method
- what is hypermedia hypermedia controls hypermedia formats
- Enterprise Integration Using REST
本文作者:cyningsun
本文地址: https://www.cyningsun.com/06-29-2020/how-to-write-restful-api.html
版權聲明:本博客所有文章除特別聲明外,均採用 CC BY-NC-ND 3.0 CN 許可協議。轉載請註明出處!