來自HeroKu的HTTP API 設計指南(中文版)

jopen 10年前發布 | 45K 次閱讀 HTTP API 網絡技術

簡介

本指南中文翻譯者為 @Easy ,他是國內首家互聯網人才拍賣網站 JobDeer.com 的創始人。轉載請保留本信息。

本指南描述了一系列 HTTP+JSON API 的設計實踐, 來自并展開于 Heroku Platform API 的工作。本指南指導著Heroku內部API的開發,我們希望也能對Heroku以外的API設計者有所幫助。

...

目錄

基礎

  • 總是使用TLS
  • 在Accepts頭中帶上版本號
  • 通過Etags支持緩存
  • 用Request-Ids追蹤請求
  • 用Ranges來分頁

請求

  • 返回適當的狀態碼
  • 總是返回完整的資源
  • 在請求body中接收JSON序列
  • 使用一致的路徑格式
  • 小寫所有路徑和屬性
  • 支持非ID的參數作為快捷方式
  • 少用路徑嵌套

響應

  • 總是提供資源(UU)ID
  • 提供標準的時間戳
  • 使用ISO8601格式的UTC時間
  • 嵌入外鍵數據
  • 總是生成結構化的錯誤信息
  • 顯示頻率限制的狀態
  • 在所有的響應中壓縮JSON數據

文檔及其他

  • 提供機器可讀的JSON格式
  • 提供人類可讀的文檔
  • 提供可執行的示例
  • 描述穩定性

基礎

總是使用TLS

總是使用TLS(就是https)來訪問API,沒有必要指出什么時候需要用,什么時候不需要用,只管任何時候都用它就好。

對所有非TLS的請求返回403 Forbidden,不要用重定向,這會允許一些不良的客戶端行為,而又沒有任何好處。依賴重定向的客戶端會使流量翻倍,而讓TLS毫無意義 —— 敏感數據已經在第一次請求時發送出來了。

在Accepts頭中帶上版本號

從一開始就為API分配版本。使用Accepts頭來發送版本信息,可以使用自定義的內容類型,如:

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

不要提供默認版本,而由客戶端顯式指定它使用哪一個特定的版本。

通過Etags支持緩存

在所有的請求中帶上 ETag 頭 , 用于識別特定版本的返回資源。用戶可以在隨后的請求中通過提供If-None-Match頭的值來檢查內容是否過期。

用Request-Ids追蹤請求

在每個API相應中提供Request-Id頭,帶上一個唯一的UUID值。如果服務器和客戶端都記錄了這些值,在跟蹤和調試請求時會派上大用場。

用Ranges來分頁

對所有可能產生大量數據的響應進行分頁。使用Content-Range 頭來標記分頁請求。可以參考這個例子,來了解請求和響應頭、狀態碼、Limit、排序和翻頁:Heroku Platform API on Ranges

請求

返回適當的狀態碼

為每個請求返回適當的狀態碼,成功的請求應該遵守如下規則:

  • 200: 當GET請求成功完成,DELETE或者PATCH請求同步完成。

  • 201: 同步方式成功完成POST請求。

  • 202: POST,DELETE或者PATCH請求提交成功,稍后將異步的進行處理。

  • 206: GET請求成功完成,但只返回了部分數據。參見用ranges分頁

注意認證和認證錯誤的使用:

  • 401 Unauthorized: 請求失敗,因為用戶沒有進行認證。
  • 403 Forbidden: 請求失敗,因為用戶被認定沒有訪問特定資源的權限。

返回合適的狀態碼可以為錯誤提供更多的信息:

  • 422 Unprocessable Entity: 你的請求服務器可以理解,但是其中包含了不合法的參數。
  • 429 Too Many Requests: 請求頻率超配,稍后再試。
  • 500 Internal Server Error: 服務器出錯了,檢查網站的狀態,或者報告問題。

根據HTTP response code 規范的指導來設計用戶錯誤和服務器錯誤情況下的狀態碼。

總是返回完整的資源

對于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響應則不用包含完整的資源,如:

$ curl -X DELETE \  
  https://service.com/apps/1f9b/dynos/05bd HTTP/1.1 202 Accepted
Content-Type: application/json;charset=utf-8
...
{}

在請求body中接收JSON序列

不要將額外信息放到form-encoded里邊,而是將其JSON序列放到PUT,PATCH或POST請求的Body中。這樣才能和同為JSON序列的響應Body對稱(作者你是處女座么),如:

$ 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"
  },
  ...
}

使用一致的路徑格式

資源名稱

使用復數來命名資源,除非該資源在系統中是單件(比如,在絕大多數系統中,一個用戶只能擁有一個賬戶)。這樣在你引用特定資源時可以保持一致性。

動作

對獨有的資源使用不需要特定動作的endpoint格式。這樣當需要特定的動作,只需要把它們放到標準的actions前綴后邊,就可以清晰的描述它們:

/resources/:resource/actions/:action

如:

/runs/{run_id}/actions/stop

小寫所有路徑和屬性

使用小寫字母和減號命名路徑,這樣Hostname可以對齊(作者你真的是處女座):

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

同樣小寫屬性,但使用下劃線來分割,這樣屬性名在JavaScript中可以不用加引號:

service_class: "first"

支持非ID的參數作為快捷方式

有時候要求最終用戶提供ID來表示資源會比較麻煩,比如,用戶可能只想得起Heroku的Appname,而應用本身卻是由UUID來區分的。在這種情況下,我們可以同時接收ID和Name:

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

絕不要只接收名稱來排除某些ID。

少用路徑嵌套

在嵌套了父子資源的數據模型中,路徑可能深度嵌套:

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

可以通過從根路徑定位來限制嵌套層數。使用嵌套來標識作用域內部的數據集。比如,上邊那個dyno屬于一個app,而app又屬于一個org的例子:

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

響應

總是提供資源(UU)ID

為每個資源提供默認的ID屬性。除非有特殊理由,總是使用UUID。不要用那些在服務的實例間或資源間不全局唯一的ID,特別是自增ID。

以8-4-4-4-12的格式小寫UUID:

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

提供標準的時間戳

為資源提供默認的 created_at 和 updated_at 時間戳:

{
  ...
  "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"

嵌入外鍵數據

將外鍵引用通過序列化的嵌入對象顯示:

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

而不是這樣:

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

這使得我們可以在inline使用相關的數據,而不需要改變響應的格式,或者引入更多高層的響應字段:

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

總是生成結構化的錯誤信息

為錯誤生成一致的,結構化的響應Body。包含機器可讀的id,人類可讀的message,以及可選的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撰寫文檔。

顯示頻率限制的狀態

對客戶端的頻率限制可以保護服務的健康,并對其他的客戶端提供高質量的服務。你可以使用token bucket 算法 來量化請求限制。

在每次請求的響應頭中,通過RateLimit-Remaining 返回剩余的請求次數。

在所有的響應中壓縮JSON數據

額外的空格增大了響應的大小,而很多人性化的客戶端可以自動美化JSON輸出。所以最好將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"
}

你可以考慮提供一個可選的方式來為客戶端輸出更長的響應,比如通過請求參數(如?pretty=true)或者通過 Accept頭(如Accept: application/vnd.heroku+json; version=3; indent=4;)。

文檔及其他

提供機器可讀的JSON格式

提供機器可讀的schema來描述你的API,可以用prmd來管理你的schema,用過prmd verify來確保它通過驗證。

提供人類可讀的文檔

提供人類可讀的文檔幫助客戶端開發者們理解你的API。

如果你使用了prmd來創建schema,那么你可以簡單的通過prmd doc命令來生成Markdown的endpoint級別的文檔。

除了endpoint級別的描述,還要提供概要級別的信息,比如:

  • 授權,包括獲得和使用授權Token。
  • API的穩定性和版本,包括如何選擇現有的API版本。
  • 通用請求和響應頭。
  • 錯誤的序列化格式。
  • 各種語言的客戶端如何使用API的例子。

提供可執行的示例

提供可執行的例子,這樣用戶可以直接在終端輸入并看到可以用的API請求。最好的情況是,這些例子可以直接復制粘貼,以最小化用戶試用API的成本,如:

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

如果你使用prmd來生成Markdown文檔,你就免費獲得了可執行的示例。

描述穩定性

描述你API的穩定性,以及哪些endpoint依賴于其成熟度,比如使用prototype,development或者production的標識。

可參考 Heroku API compatibility policy 了解哪些接口是穩定的,哪些可能有變動。

一旦你的API宣布為 production-ready 和 穩定版,不要在該API版本上做任何不向前兼容的修改。如果你需要做不向前兼容的修改,創建一個新的版本號。

英文原版 → https://github.com/interagent/http-api-design

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!