HTTP API 設計指南

英文原文: HTTP API Design Guide
本文譯者: LeoXu, Garfielt, 無若, --zxphtml


介紹

本指南描述了一套有關 HTTP+JSON API 的設計實踐, 原始內容提取自 Heroku 平臺 API 的工做.git

本指南是對API的補充,也是Heroku新的內部API的指南. 咱們但願引發Heroku以外的API設計者的興趣.github

這裏咱們的目標是一致的,專一於業務邏輯而避免脫節的設計. 咱們就是要尋找一個良好的,一致的,文檔優良的方式來設計API,而不必是惟一理想的方式.算法

咱們假定你熟悉HTTP+JSON API的一些基礎,不會再指南中涵蓋全部基礎性的東西.shell

咱們歡迎爲這一指南 作出貢獻.json

返回適當的狀態碼

對於每一種響應返回適當的HTTP狀態碼. 成功的響應應該根據下面的指南編碼:api

  • 200: GET調用請求成功, 以及DELETE 或者 PATCH 調用同步完成
  • 201: 同步完成的POST調用請求成功
  • 202: 請求接受一個將會被同步處理的POST,DELETE或者PATCH調用
  • 206: GET請求成功,但只有部分響應返回: 見 上述有關範圍的內容

請閱讀指導有關用戶錯誤和服務器錯誤狀況的狀態碼的HTTP 響應碼文檔緩存

提供可用的完整資源

儘量在響應中提供完整的資源描述 (例如,帶有全部屬性的對象). 老是在200和201響應中提供完整的資源, 包括 PUT/PATCH 和 DELETE 請求, 例如:服務器

$ curl -X DELETE \  
  https://service.com/apps/1f9b/domains/0fd4

HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8
...
{
  "created_at": "2012-01-01T12:00:00Z",
  "hostname": "subdomain.example.com",
  "id": "01234567-89ab-cdef-0123-456789abcdef",
  "updated_at": "2012-01-01T12:00:00Z"
}

202 響應不會包含完整的資源描述,例如:app

$ curl -X DELETE \  
  https://service.com/apps/1f9b/dynos/05bd

HTTP/1.1 202 Accepted
Content-Type: application/json;charset=utf-8
...
{}

接受請求中序列化的JSON

接受PUT/PATCH/POST請求中的序列化JSON, 做爲表單編碼數據的替代或者補充. 這樣就能夠建立對稱的JSON序列化響應,例如:

$ curl -X POST https://service.com/apps \
    -H "Content-Type: application/json" \
    -d '{"name": "demoapp"}'

{
  "id": "01234567-89ab-cdef-0123-456789abcdef",
  "name": "demoapp",
  "owner": {
    "email": "username@example.com",
    "id": "01234567-89ab-cdef-0123-456789abcdef"
  },
  ...
}

提供資源 (UU)ID

默認給每個資源都指定一個id. 除非你有更好的理由,否則就使用UUID. 不要使用在整個服務或者服務中其它資源那裏不是全局惟一的ID, 特別是自增加的ID.

用小寫 8-4-4-4-12 格式生成UUID,例如:

"id": "01234567-89ab-cdef-0123-456789abcdef"

提供標準的時間戳

默認爲資源提供建立和更新的時間戳,例如:

{
  ...
  "created_at": "2012-01-01T12:00:00Z",
  "updated_at": "2012-01-01T13:00:00Z",
  ...}

這些時間戳可能對一些資源沒啥用,如此則能夠省去.

使用ISO8601中的UTC時間格式

只使用UTC接收和返回時間. 使用 ISO8601 格式來生成時間,例如:

"finished_at": "2012-01-01T12:00:00Z"

使用一致的路徑格式

資源名稱

使用資源名稱的複數形式,除非系統中相關的資源是惟一的(例如,在大多數系統,用戶的帳戶永遠都只能有一個). 這就能在你引用特定的資源時保持一致的方式.

操做

首選端點佈局,由於它不須要對單獨的資源有任何特殊的操做. 有些狀況下是須要特殊操做的,那就把它們放在一個標準的前綴下,以清楚的界定它們:

/resources/:resource/actions/:action

例如.

/runs/{run_id}/actions/stop

小寫的路徑和屬性

使用小寫和用虛線符號分隔的路徑名,便於同主機名對齊, 例如:

service-api.com/users
service-api.com/app-setups

屬性一樣也使用小寫,可是使用下劃線作分隔,那就屬性名在Javascript中就能夠不用引號了, 例如:

service_class: "first"

內聯外鍵關係

使用一個內聯的對象來序列化外鍵引用,例如:

{
  "name": "service-production",
  "owner": {
    "id": "5d8201b0..."
  },
  ...}

而不是以下例:

{
  "name": "service-production",
  "owner_id": "5d8201b0...",
  ...}

這種方式使得在沒必要改變響應結構或者引入更多頂級響應域的前提下內聯如更多相關資源的信息,例如:

{
  "name": "service-production",
  "owner": {
    "id": "5d8201b0...",
    "name": "Alice",
    "email": "alice@heroku.com"
  },
  ...}

支持爲方便起見的非id間接引用

在某些狀況下對於端用戶而言提供一個ID標誌一個資源可能會方便些。例如,一個用戶會須要一個Heroku應用名稱,但那個應用時用UUID標識的。在這些狀況下你可能想要同時接受名稱和ID,例如:

$ curl https://service.com/apps/{app_id_or_name}
$ curl https://service.com/apps/97addcf0-c182
$ curl https://service.com/apps/www-prod

不要只接受名稱而排斥ID.

生成結構性的錯誤

使用一致的,結構化的錯誤響應. 包括一個依賴於機器的錯誤id,一我的類可讀的錯誤消息,以及可選的一個指出有關該錯誤及如何解決的信息的url,例如:

HTTP/1.1 429 Too Many Requests
{
  "id":      "rate_limit",
  "message": "Account reached its API rate limit.",
  "url":     "https://docs.service.com/rate-limits"}

爲你的錯誤格式,以及客戶端可能會遇到的錯誤id編寫文檔.

支持使用Etag的緩存

在全部的響應中包含一個ETag頭,以標識返回資源的特定版本. 用戶就可以從If-None-Match頭獲取的值中檢查出他們的後續請求的是否已通過時.

使用Request-Id跟蹤請求

在每個API響應中包含一個Request-Id頭,填充一個UUID值。若是服務器和客戶端都記錄了這個值的話,它就能在跟蹤和調試請求方面起到做用.

使用範圍進行分頁

對容易產生大量數據的響應進行分頁. 使用 Content-Range 頭來傳送分頁請求. 詳細的能夠看看 Heroku 平臺有關範圍的API 中的請求和響應頭, 狀態碼, 限制,排序和分頁瀏覽的示例.

展現速率限制狀態

來自客戶端的速率限制請求用以保護服務的健康,併爲其它的客戶端保持較高的服務質量. 你可使用一種 令牌桶算法 來量化請求限制.

能夠在RateLimit-Remaining響應頭中返回每一個請求的剩餘請求令牌數量.

帶有版本的接收頭

從一開始就要對API進行版本話。使用接收頭,以及一個自定義內容類型來同版本進行交互,例如:

Accept: application/vnd.heroku+json; version=3

不去指定一個默認的版本,而不是要求客戶端明確指定它們要使用一個特定的版本.

最小化路徑內聯

在帶有內聯父/子資源關係的數據模型中,路徑可能會內聯得很深,例如:

/orgs/{org_id}/apps/{app_id}/dynos/{dyno_id}

能夠經過在根路徑定位資源來限制內聯深度. 使用內聯來指定範圍集合.例如,上述狀況中一個dyno就屬於一個屬於org的app:

/orgs/{org_id}
/orgs/{org_id}/apps
/apps/{app_id}
/apps/{app_id}/dynos
/dynos/{dyno_id}

提供機器可讀的JSON模式

提供一個機器可讀的模式能夠精確的指定你的API。使用 prmd 來管理你的模式,並確保它能被prmd verify驗證.

提供人類可讀的文檔

提供客戶端開發者能夠用來理解你的API的人類可讀文檔.

若是你使用prmd建立了一個如上所述的模式,那麼你就能夠很容易的使用prmd doc來爲全部的端點生成Markdown文檔.

除了端點的詳細信息以外,還要提供API概述的一些信息:

  • 認證,包括獲取和使用認證令牌.
  • API 穩定性和版本,包括如何選擇理想的API版本.
  • 通用的請求和響應頭.
  • 錯誤的序列化格式.
  • 用不一樣的語言使用API的客戶端的示例.

提供可執行的示例

提供可執行的示例,用戶能夠直接在終端中敲入命令來查看API的調用如何運行. 爲了儘量的擴展,這些示例應該要能夠照字面意義使用,以最小化用戶嘗試這些API所須要作的事情,例如:

$ export TOKEN=... # acquire from dashboard
$ curl -is https://$TOKEN@service.com/users

若是你使用 prmd 生成了Markdown文檔,你就將能夠不費力的得到每個端點的示例.

穩定性描述

描述API的穩定性或者依據其成熟性和穩定性的各個點,例如:以原型/開發/成品做爲標誌節點。

查看Heroku API兼容性策略爲穩定性和變動管理方法提供一種可能。

一旦你的API被定義爲是爲生產所準備的和堅固的,那當API版本改變的時候,要使得這些API能有向後的兼容性。在建立一個新的API時,若是你須要作向後不兼容的變動,應增長版本號。

SSL需求

無一例外,要SSL去訪問API時,不管用不用SSL,都沒必要找出以及解釋其緣由,它們就是須要SSL。

良好打印的默認json

用戶第一次查看你的api極可能是在使用curl的命令行裏。若是API的響應有良好的打印格式,那在命令行裏它們會很容易理解。爲了給這些開發者提供方便,良好打印格式的JSON以下:

{
  "beta": false,
  "email": "alice@heroku.com",
  "id": "01234567-89ab-cdef-0123-456789abcdef",
  "last_login": "2012-01-01T12:00:00Z",
  "created_at": "2012-01-01T12:00:00Z",
  "updated_at": "2012-01-01T12:00:00Z"}

而不是:

{"beta":false,"email":"alice@heroku.com","id":"01234567-89ab-cdef-0123-456789abcdef","last_login":"2012-01-01T12:00:00Z", "created_at":"2012-01-01T12:00:00Z","updated_at":"2012-01-01T12:00:00Z"}

要確保在JSON結尾有換行,以防止阻塞用戶的終端界面。

對於大部分API的響應,性能考濾要優先於良好打印。在某些結點(例如高流量結點)或爲某些特定用戶(例如無GUI界面的程序)使用時,你可能會考濾使用高性能而非良好打印的API。

注:headless program譯爲「無顯示界面的程序」,參考自這篇文章.

相關文章
相關標籤/搜索