Restful API 设计规范实战
Restful API 設計規范
使用的名詞而不是動詞
不應該使用動詞:
/getAllResources
/createNewResources
/deleteAllResources
GET方法和查詢參數不能改變資源狀態:
如果要改變資源的狀態,使用PUT、POST、DELETE。下面是錯誤的用GET方法來修改user的狀態:
GET /users/711?activate GET /users/711/activateRest的核心原則是將你的API拆分為邏輯上的資源。這些資源通過HTTP被操作(GET,POST,PUT,DELETE)
我們定義資源ticket、user、group:
GET /tickets # 獲取ticket列表
GET /tickets/12 # 查看某個具體的ticket
POST /tickets # 新建一個ticket
PUT /tickets/12 #新建ticket 12
DELETE /tickets/12 # 刪除ticket 12
只需要一個endpoint:/tickets,再也沒有其他什么命名規則和url規則了。
一個可以遵循的規則是:雖然看起來使用復數來描述某一個資源看起來特別扭,但是統一所有的endpoint,使用復數使得你的URL更加規整。這讓API使用者更加容易理解,對開發者來說也更容易實現。
處理關聯:
GET /tickets/12/messages # 獲取ticket 12的message列表
GET /tickets/12/messages/5 #獲取ticket 12的message 5
POST /tickets/12/messages 創建ticket 12的一個message
PUT /tickets/12/messages/5 更新ticket 12的message 5
DELETE /tickets/12/messages/5 刪除ticket 12的message 5
避免層級過深的URI
/ 在url中表達層級,用于按實體關聯關系進行對象導航,一般根據id導航。
過深的導航容易導致url膨脹,不易維護,如 GET /zoos/1/areas/3/animals/4,盡量使用查詢參數代替路勁中的實體導航,如GET /animals?zoo=1&area=3。
結果過濾,排序,搜索
url最好越簡短越好,對結果過濾、排序、搜索相關的功能都應該通過參數實現。
過濾:例如你想限制GET /tickets 的返回結果:只返回那些open狀態的ticket, GET /tickets?state=open 這里的state就是過濾參數。
排序:和過濾一樣,一個好的排序參數應該能夠描述排序規則,而不和業務相關。復雜的排序規則應該通過組合實現。排序參數通過 , 分隔,排序參數前加 - 表示降序排列。
GET /tickets?sort=-priority #獲取按優先級降序排列的ticket列表
GET /tickets?sort=-priority,created_at #獲取按優先級降序排列的ticket列表,在同一個優先級內,先創建的ticket排列在前面。
搜索:有些時候簡單的排序是不夠的。我們可以使用搜索技術來實現
GET /tickets?q=return&state=open&sort=-priority,create_at # 獲取優先級最高且打開狀態的ticket,而且包含單詞return的ticket列表。
限制API返回值的域
有時候API使用者不需要所有的結果,在進行橫向限制的同時(例如值返回API結果的前十個),還應該可以進行縱向限制,并且這個功能能有效的提高網絡帶寬使用率和速度。可以使用fields查詢參數來限制返回的域例如:
GET /tickets?fields=id,subject,customer_name,updated_at&state=open&sort=-updated_at
Response不要包裝
response 的 body直接就是數據,不要做多余的包裝。錯誤實例:
{"success":true,"data":{"id":1, "name":"xiaotuan"} }更新和創建操作應該返回資源
在POST操作以后,返回201created 狀態碼,并且包含一個指向新資源的url作為返回頭。
命名方式
是蛇形命名還是駝峰命名?如果使用json那么最好的應該是遵守JavaScript的命名方法-駝峰命名法。Java、C# 使用駝峰,python、ruby使用蛇形。
默認使用pretty print格式,開啟gzip
開啟pretty print返回結果會更加友好易讀,而且額外的傳輸也可以忽略不計。如果忘了使用gzip那么傳輸效率將會大大減少,損失大大增加。
GitHub v3S實踐經驗
1.Current Version
通過Accept字段來區分版本號,而不是在url中嵌入版本號:
Accept: application/vnd.github.v3+json
2.Schema
Summary Representation
當你請求獲取某一資源的列表時,響應僅返回資源的屬性子集。有些屬性對API來說代價是非常高的,出于性能的考慮,會排除這些屬性。要獲取這些屬性,請求"detailed" representation。
Example:當你獲取倉庫的列表時,你獲得的是每個倉庫的summary representation。
GET /orgs/octokit/reposDetailed Representation
當你獲取一個單獨的資源時,響應會返回這個資源的所有屬性。
Example:當你獲取一個單獨的倉庫,你會獲得這個倉庫的detailed representation。
GET /repos/octokit/octokit.rb3.Parameters
許多API都帶有可選參數。對于GET請求,任何不作為路徑構成部分的參數都可以通過HTTP查詢參數傳入。
GET https://api.github.com/repos/vmg/redcarpet/issues?state=closed在這個例子中,'vmg' 和 'redcarpet' 作為 :owner 和 :repo 的參數,而 :state 作為查詢參數。
對于POST、PATCH、PUT和DELETE的請求,不包含在URL中的參數需要編碼成JSON傳遞,且 Content-Type為 'application/json'。
Root Endpoint
你可以對根節點GET請求,獲取根節點下的所有API分類。
Client Errors
有三種可能的客戶端錯誤,在接收到請求體時:
1 發送非法JSON會返回 400 Bad Request.
HTTP/1.1 400 Bad Request Content-Length: 35{"message":"Problems parsing JSON"}2 發送錯誤類型的JSON值會返回 400 Bad Request.
HTTP/1.1 400 Bad Request Content-Length: 40{"message":"Body should be a JSON object"}3 發送無效的值會返回 422 Unprocessable Entity.
HTTP/1.1 422 Unprocessable Entity Content-Length: 149{"message": "Validation Failed","errors": [{"resource": "Issue","field": "title","code": "missing_field"}] }我們可以告訴發生了什么錯誤,下面是一些可能的驗證錯誤碼:
| missing | 資源不存在 |
| missing_field | 資源必需的域沒有被設置 |
| invalid | 域的格式非法 |
| already_exists | 另一個資源的域的值和此處的相同,這會發生在資源有唯一的鍵的時候 |
HTTP Redirects
API v3在合適的地方使用HTTP重定向。客戶端應該假設任何請求都會導致重定向。重定向在響應頭中有一個 Location 的域,此域包含了資源的真實位置。
HTTP Verbs
API v3力爭使用正確的HTTP動詞來表示每次請求。
| HEAD | 對任何資源僅請求頭信息 |
| GET | 獲取資源 |
| POST | 創建資源 |
| PATCH | 使用部分的JSON數據更新資源 |
| PUT | 取代資源或資源集合 |
| DELETE | 刪除資源 |
Hypermedia
很多資源有一個或者更多的 *_url 屬性指向其他資源。這意味著服務端提供明確的URL,這樣客戶端就不必要自己構造URL了。
Pagination
請求資源列表時會進行分頁,默認每頁30個。當你請求后續頁的時候可以使用 ?page 參數。對于某些資源,你可以通過參數 ?per_page自定義每頁的大小。
curl 'https://api.github.com/user/repos?page=2&per_page=100'需要注意的一點是,頁碼是從1開始的,當省略參數 ?page 時,會返回首頁。
Basics of Pagination
關于分頁的其他相關信息在響應的頭信息的 Link 里提供。比如,去請求一個搜索的API,查找Mozilla的項目中哪些包含詞匯addClass :
curl -I "https://api.github.com/search/code?q=addClass+user:mozilla"頭信息中Link字段如下:
Link: <https://api.github.com/search/code?q=addClass+user%3Amozilla&page=2>; rel="next", <https://api.github.com/search/code?q=addClass+user%3Amozilla&page=34>; rel="last"rel="next" 表示下一頁是 page=2。也就是說,默認情況下所有的分頁請求都是從首頁開始。rel="last" 提供更多信息,表示最后一頁是34。即我們還有33頁的信息包含addClass。
總之,我們應該依賴于Link提供的信息,而不要嘗試自己去猜或者構造URL。
Navigating through the pages
既然已經知道會接收多少頁面,我們可以通過頁面導航來消費結果。我們可以通過傳遞一個page參數,例如跳到14頁:
curl -I "https://api.github.com/search/code?q=addClass+user:mozilla&page=14"這是頭信息中Link字段:
Link: <https://api.github.com/search/code?q=addClass+user%3Amozilla&page=15>; rel="next",<https://api.github.com/search/code?q=addClass+user%3Amozilla&page=34>; rel="last",<https://api.github.com/search/code?q=addClass+user%3Amozilla&page=1>; rel="first",<https://api.github.com/search/code?q=addClass+user%3Amozilla&page=13>; rel="prev"我們會獲得更多的信息,rel="first"表示首頁,rel="prev"表示前一頁的頁碼。通過這些信息,我們可以構造一個UI界面讓用戶在first、previous、next、last之間進行跳轉。
Rate Limiting
對于認證的請求,可以每小時最多請求5000次。對于沒有認證的請求,限制在每小時60次請求。
檢查返回的HTTP頭,可以看到當前的速率限制:
curl -i https://api.github.com/users/whatever HTTP/1.1 200 OK Server: GitHub.com Date: Thu, 27 Oct 2016 03:05:42 GMT Content-Type: application/json; charset=utf-8 Content-Length: 1219 Status: 200 OK X-RateLimit-Limit: 60 X-RateLimit-Remaining: 48 X-RateLimit-Reset: 1477540017header頭信息告訴你當前的速率限制狀態:
| X-RateLimit-Limit | 當前用戶被允許的每小時請求數 |
| X-RateLimit-Remaining | 在當前發送窗口內還可以發送的請求數 |
| X-RateLimit-Reset | 按當前速率發送后,發送窗口重置的時間 |
一旦你超過了發送速率限制,你會收到一個錯誤響應:
HTTP/1.1 403 Forbidden Date: Tue, 20 Aug 2013 14:50:41 GMT Status: 403 Forbidden X-RateLimit-Limit: 60 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1377013266{"message": "API rate limit exceeded for xxx.xxx.xxx.xxx. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)","documentation_url": "https://developer.github.com/v3/#rate-limiting" }User Agent Required
所有的API請求必須包含一個有效的 User-Agent 頭。請求頭不包含User-Agent的請求會被拒絕。
Conditional requests
大多數響應都會返回一個 ETag 頭。很多響應也會返回一個 Last-Modified 頭。你可以使用這些頭信息對這些資源進行后續請求,分別使用 If-None-Match 和 If-Modified-Since頭。如果資源沒有發生改變,服務器端會返回 304 Not Modified。
Enchant REST API 實踐經驗
Requests
Limited HTTP Clients
如果你使用的HTTP客戶端不支持PUT、PATCH、DELETE方法,發送一個POST請求,頭信息里包含X-HTTP-Method-Override字段,它的值是實際需要的動詞。
$ curl -u email:password https://site.enchant.com/api/v1/users/543abc \-X POST \-H "X-HTTP-Method-Override: DELETE"Rate Limiting
所有響應的頭部包含描述當前限流狀態的字段:
Rate-Limit-Limit: 100 Rate-Limit-Remaining: 99 Rate-Limit-Used: 1 Rate-Limit-Reset: 20Rate-Limit-Limit - 當前時間段內允許的總的請求數
Rate-Limit-Remaining - 當前時間段內還剩余的請求數
Rate-Limit-Used - 本次所使用的請求數
Rate-Limit-Reset - 重置所需秒數
如果速率限制被打破,API會返回 429 Too Many Requests 的狀態碼。在這種情況下,你的應用不應該再發送任何請求直到 Rate-Limit-Reset 所規定的時間過去。
Field Filtering
你可以自己限制響應返回的域。只需要你傳遞一個 fields 參數,用逗號分隔所需要的域,比如:
GET /api/v1/users?fields=id,first_nameCounting
所有返回一個集合的URL,都會提供count統計所有結果的個數。要獲取count值需要加一個 count=true 的參數。count會在消息頭中的Total-Count 字段中返回。
GET /api/v1/tickets?count=true
200 OK Total-Count: 135 Rate-Limit-Limit: 100 Rate-Limit-Remaining: 98 Rate-Limit-Used: 2 Rate-Limit-Reset: 20 Content-Type: application/json[... results ... ]
count表示所有現存結果的數量,而不是此次響應返回的結果的數量。
Enveloping
如果你的HTTP客戶端難以讀取狀態碼和頭信息,我們可以將所有都打包進響應消息體中。我們只需要傳遞參數 envelope=true,而API會始終返回200的HTTP狀態碼。真正的狀態碼、頭信息和響應都在消息體中。
GET /api/v1/users/does-not-exist?envelope=true200 OK
{"status": 404,"headers": {"Rate-Limit-Limit": 100,"Rate-Limit-Remaining": 50,"Rate-Limit-Used": 0,"Rate-Limit-Reset": 25},"response": {"message": "Not Found"} }
其他如 分頁、排序等,enchant的設計規范和GitHub v3大致相同,不在贅述。
原文鏈接
https://segmentfault.com/a/11...
總結
以上是生活随笔為你收集整理的Restful API 设计规范实战的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Builder设计模式
- 下一篇: spring 读取配置文件的优先级