Restful API 設計規範實戰

Restful API 設計規範

使用的名詞而不是動詞

不該該使用動詞:python

/getAllResources
/createNewResources
/deleteAllResourcesgit

GET方法和查詢參數不能改變資源狀態:

若是要改變資源的狀態,使用PUT、POST、DELETE。下面是錯誤的用GET方法來修改user的狀態:github

GET /users/711?activate
GET /users/711/activate

Rest的核心原則是將你的API拆分爲邏輯上的資源。這些資源經過HTTP被操做(GET,POST,PUT,DELETE)

咱們定義資源ticket、user、group:json

  • GET /tickets # 獲取ticket列表segmentfault

  • GET /tickets/12 # 查看某個具體的ticketapi

  • POST /tickets # 新建一個ticketruby

  • PUT /tickets/12 #新建ticket 12服務器

  • DELETE /tickets/12 # 刪除ticket 12網絡

只須要一個endpoint:/tickets,再也沒有其餘什麼命名規則和url規則了。app

一個能夠遵循的規則是:雖然看起來使用複數來描述某一個資源看起來特別扭,可是統一全部的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/repos

Detailed Representation

當你獲取一個單獨的資源時,響應會返回這個資源的全部屬性。

Example:當你獲取一個單獨的倉庫,你會得到這個倉庫的detailed representation。

GET /repos/octokit/octokit.rb

3.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"
    }
  ]
}

咱們能夠告訴發生了什麼錯誤,下面是一些可能的驗證錯誤碼:

Error Name Description
missing 資源不存在
missing_field 資源必需的域沒有被設置
invalid 域的格式非法
already_exists 另外一個資源的域的值和此處的相同,這會發生在資源有惟一的鍵的時候

HTTP Redirects

API v3在合適的地方使用HTTP重定向。客戶端應該假設任何請求都會致使重定向。重定向在響應頭中有一個 Location 的域,此域包含了資源的真實位置。

HTTP Verbs

API v3力爭使用正確的HTTP動詞來表示每次請求。

Verb Description
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: 1477540017

header頭信息告訴你當前的速率限制狀態:

Header Name Description
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-MatchIf-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: 20
  • Rate-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_name

Counting

全部返回一個集合的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=true

200 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...

相關文章
相關標籤/搜索