API 設(shè)計(jì)規(guī)范

API 設(shè)計(jì)規(guī)范

基礎(chǔ)

(1) 隔離關(guān)注點(diǎn)

設(shè)計(jì)時(shí)通過將請求和響應(yīng)之間的不同部分隔離來讓事情變得簡單。保持簡單的規(guī)則讓我們能更關(guān)注在一些更大的更困難的問題上。

請求和響應(yīng)將解決一個(gè)特定的資源或集合。使用路徑(path)來表明身份,body來傳輸內(nèi)容(content)還有頭信息(header)來傳遞元數(shù)據(jù)(metadata)。查詢參數(shù)同樣可以用來傳遞頭信息的內(nèi)容,但頭信息是首選,因?yàn)樗麄兏`活、更能傳達(dá)不同的信息。

(2) 強(qiáng)制使用安全連接(Secure Connections)

所有的訪問API行為,都需要用TLS通過安全連接來訪問。沒有必要搞清或解釋什么情況需要TLS 什么情況不需要TLS,直接強(qiáng)制任何訪問都要通過 TLS。

理想狀態(tài)下,通過拒絕所有非TLS請求,不響應(yīng)http或80端口的請求以避免任何不安全的數(shù)據(jù)交換。如果現(xiàn)實(shí)情況中無法這樣做,可以返回403 Forbidden響應(yīng)。

把非TLS的請求重定向(Redirect)至TLS連接是不明智的,這種含混/不好的客戶端行為不會帶來明顯好處。依賴于重定向的客戶端訪問不僅會導(dǎo)致雙倍的服務(wù)器負(fù)載,還會使 TLS 加密失去意義,因?yàn)樵谑状畏荰LS調(diào)用時(shí),敏感信息就已經(jīng)暴露出去了。

(3) 強(qiáng)制頭信息Accept中提供版本號

制定版本并在版本之間平緩過渡對于設(shè)計(jì)和維護(hù)一套API是個(gè)巨大的挑戰(zhàn)。所以,最好在設(shè)計(jì)之初就使用一些方法來預(yù)防可能會遇到的問題。

為了避免API的變動導(dǎo)致用戶使用中產(chǎn)生意外結(jié)果或調(diào)用失敗,最好強(qiáng)制要求所有訪問都需要指定版本號。請避免提供默認(rèn)版本號,一旦提供,日后想要修改它會相當(dāng)困難。

最適合放置版本號的位置是頭信息(HTTP Headers),在 Accept 段中使用自定義類型(contenttype)與其他元數(shù)據(jù)(metadata)一起提交。例如:

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

(4) 支持ETag緩存

在所有返回的響應(yīng)中包含ETag頭信息,用來標(biāo)識資源的版本。這讓用戶對資源進(jìn)行緩存處理成為可能,在后續(xù)的訪問請求中把If-None-Match頭信息設(shè)置為之前得到的ETag值,就可以偵測到已緩存的資源是否需要更新。

(5) 為內(nèi)省而提供Request-Id

為每一個(gè)請求響應(yīng)包含一個(gè)Request-Id頭,并使用UUID作為該值。通過在客戶端、服務(wù)器或任何支持服務(wù)上記錄該值,它能為我們提供一種機(jī)制來跟蹤、診斷和調(diào)試請求。

(6) 通過請求中的范圍(Range)拆分大的響應(yīng)

一個(gè)大的響應(yīng)應(yīng)該通過多個(gè)請求使用Range頭信息來拆分,并指定如何取得。詳細(xì)的請求和響應(yīng)的頭信息(header),狀態(tài)碼(status code),范圍(limit),排序(ordering)和迭代(iteration)等,參考Heroku PlatformAPI discussion of Ranges.

請求(Requests)

(1) 在請求的body體使用JSON格式數(shù)據(jù)

在 PUT/PATCH/POST 請求的正文(request bodies)中使用JSON格式數(shù)據(jù),而不是使用form 表單形式的數(shù)據(jù)。這與我們使用JSON格式返回請求相對應(yīng),例如:

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

(2) 使用統(tǒng)一的資源路徑格式

資源名(Resource names):使用復(fù)數(shù)形式為資源命名,除非這個(gè)資源在系統(tǒng)中是單例的 (例如,在大多數(shù)系統(tǒng)中,給定的用戶帳戶只有一個(gè))。 這種方式保持了特定資源的統(tǒng)一性。

行為(Actions):好的末尾不需要為資源指定特殊的行為,但在特殊情況下,為某些資源指定行為卻是必要的。為了描述清楚,在行為前加上一個(gè)標(biāo)準(zhǔn)的actions:

/resources/:resource/actions/:action 

例如:

/runs/{run_id}/actions/stop 

(3) 路徑和屬性要小寫

為了和域名命名規(guī)則保持一致,使用小寫字母并用 - 分割路徑名字,例如:

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

屬性也使用小寫字母,但是屬性名要用下劃線 _ 分割,以便在Javascript****中省略引號。例如:

service_class: "first"

(4) 支持方便的無id間接引用

在某些情況下,讓用戶提供ID去定位資源是不方便的。例如,一個(gè)用戶想取得他在Heroku平臺app信息,但是這個(gè)app的唯一標(biāo)識是UUID。這種情況下,你應(yīng)該支持接口通過名字和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。

(5) 最小化路徑嵌套

在一些有父路徑/子路徑嵌套關(guān)系的資源數(shù)據(jù)模塊中,路徑可能有非常深的嵌套關(guān)系,例如:

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

推薦在根(root)路徑下指定資源來限制路徑的嵌套深度。使用嵌套指定范圍的資源。在上述例子中,dyno屬于app,app屬于org可以表示為:

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

響應(yīng)(Responses)

(1) 返回合適的狀態(tài)碼

為每一次的響應(yīng)返回合適的HTTP狀態(tài)碼。好的響應(yīng)應(yīng)該使用如下的狀態(tài)碼:

200: GET請求成功,及DELETE或PATCH同步請求完成,或者PUT同步更新一個(gè)已存在的資源;

201: POST同步請求完成,或者PUT同步創(chuàng)建一個(gè)新的資源;

202: POST,PUT,DELETE,或PATCH請求接收,將被異步處理;

206: GET 請求成功,但是只返回一部分;

使用身份認(rèn)證(authentication)和授權(quán)(authorization)錯(cuò)誤碼時(shí)需要注意:

401 Unauthorized: 用戶未認(rèn)證,請求失敗;

403 Forbidden: 用戶無權(quán)限訪問該資源,請求失敗;

當(dāng)用戶請求錯(cuò)誤時(shí),提供合適的狀態(tài)碼可以提供額外的信息:

422 Unprocessable Entity: 請求被服務(wù)器正確解析,但是包含無效字段;

429 Too Many Requests: 因?yàn)樵L問頻繁,你已經(jīng)被限制訪問,稍后重試;

500 Internal Server Error: 服務(wù)器錯(cuò)誤,確認(rèn)狀態(tài)并報(bào)告問題.

對于用戶錯(cuò)誤和服務(wù)器錯(cuò)誤情況狀態(tài)碼,參考: ****HTTP response code spec

(2) 提供全部可用的資源

提供全部可顯現(xiàn)的資源表述 (例如:這個(gè)對象的所有屬性) ,當(dāng)響應(yīng)碼為200或是201時(shí)返回所有可用資源,包含 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" 
}

當(dāng)請求狀態(tài)碼為202時(shí),不返回所有可用資源,例如:

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

(3) 提供資源的(UU)ID

在默認(rèn)情況給每一個(gè)資源一個(gè)id屬性。除非有更好的理由,否則請使用UUID。不要使用那種在服務(wù)器上或是資源中不是全局唯一的標(biāo)識,尤其是自動增長的id。

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

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

(4) 提供標(biāo)準(zhǔn)的時(shí)間戳

為資源提供默認(rèn)的創(chuàng)建時(shí)間 created_at 和更新時(shí)間 updated_at,例如:

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

有些資源不需要使用時(shí)間戳那么就忽略這兩個(gè)字段。

(5) 使用UTC(世界標(biāo)準(zhǔn)時(shí)間)時(shí)間,用ISO8601進(jìn)行格式化

僅接受和返回UTC格式的時(shí)間。ISO8601格式的數(shù)據(jù),例如:

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

(6) 嵌套外鍵關(guān)系

使用嵌套對象序列化外鍵關(guān)聯(lián),例如:

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

而不是像這樣:

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

這種方式盡可能的把相關(guān)聯(lián)的資源信息內(nèi)聯(lián)在一起,而不用改變資源的結(jié)構(gòu),或者引入更多的頂層字段,例如:

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

(7) 生成結(jié)構(gòu)化的錯(cuò)誤

響應(yīng)錯(cuò)誤的時(shí),生成統(tǒng)一的、結(jié)構(gòu)化的錯(cuò)誤信息。包含一個(gè)機(jī)器可讀的錯(cuò)誤 id,一個(gè)人類可讀的錯(cuò)誤信息(message),根據(jù)情況可以添加一個(gè)url來告訴客戶端關(guān)于這個(gè)錯(cuò)誤的更多信息以及如何去解決它,例如:

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

文檔化錯(cuò)誤信息格式,以及客戶端可能遇到的錯(cuò)誤信息id。

(8) 顯示頻率限制狀態(tài)

客戶端的訪問速度限制可以維護(hù)服務(wù)器的良好狀態(tài),保證為其他客戶端請求提供高性的服務(wù)。你可以使用token bucket algorithm技術(shù)量化請求限制。

為每一個(gè)帶有RateLimit-Remaining響應(yīng)頭的請求,返回預(yù)留的請求tokens。

(9) 保證響應(yīng)JSON最小化

請求中多余的空格會增加響應(yīng)大小,而且現(xiàn)在很多的HTTP客戶端都會自己輸出可讀格式("prettify")的JSON。所以最好保證響應(yīng)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" 
 }

你可以提供可選的方式為客戶端提供更詳細(xì)可讀的響應(yīng),使用查詢參數(shù)(例如:?pretty=true)或者通過Accept頭信息參數(shù)(例如:Accept:application/vnd.heroku+json;version=3; indent=4;)。

工件(Artifacts)

(1)提供機(jī)器可讀的JSON模式

提供一個(gè)機(jī)器可讀的模式來恰當(dāng)?shù)谋憩F(xiàn)你的API。使用 prmd管理你的模式,并且確保用prmd verify驗(yàn)證是有效的。

(2)提供人類可讀的文檔

提供人類可讀的文檔讓客戶端開發(fā)人員可以理解你的API。

如果你用prmd創(chuàng)建了一個(gè)概要并且按上述要求描述,你可以為所有節(jié)點(diǎn)很容易的使用prmd doc生成Markdown文檔。

除了節(jié)點(diǎn)信息,提供一個(gè)API概述信息:

  • 驗(yàn)證授權(quán),包含如何取得和如何使用token。
  • API穩(wěn)定及版本管理,包含如何選擇所需要的版本。
  • 一般情況下的請求和響應(yīng)的頭信息。
  • 錯(cuò)誤的序列化格式。
  • 不同編程語言客戶端使用API的例子。

(3) 提供可執(zhí)行的例子

提供可執(zhí)行的示例讓用戶可以直接在終端里面看到API的調(diào)用情況,最大程度的讓這些示例可以簡單的使用,以減少用戶嘗試使用API的工作量。例如:

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

如果你使用prmd生成Markdown文檔,每個(gè)節(jié)點(diǎn)都會自動獲取一些示例。

(4)描述穩(wěn)定性

描述您的API的穩(wěn)定性或是它在各種各樣節(jié)點(diǎn)環(huán)境中的完備性和穩(wěn)定性,例如:加上原型版(prototype)/開發(fā)版(development)/產(chǎn)品版(production)等標(biāo)記。

更多關(guān)于可能的穩(wěn)定性和改變管理的方式,查看 ****Heroku API compatibility policy

一旦你的API宣布產(chǎn)品正式版本及穩(wěn)定版本時(shí),不要在當(dāng)前API版本中做一些不兼容的改變。如果你需要,請創(chuàng)建一個(gè)新的版本的API。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容