Restful Api 實戰設計指南

1. Well REST Api

2. 基本原則

2.1. 關注點分離

關注點分離(Separation of concerns,SOC)是對只與「特定概念、目標」(關注點)相關聯的軟件組成部分進行「標識、封裝和操縱」的能力,即標識、封裝和操縱關注點的能力。是處理複雜性的一個原則。因爲關注點混雜在一塊兒會致使複雜性大大增長,因此可以把不一樣的關注點分離開來,分別處理就是處理複雜性的一個原則,一種方法。-- 維基百科
  • 大問題拆分紅個個小問題
  • 萬能接口拆分紅獨立的小接口

Jietu20180830-135439.jpg

2.2. 一致性

  • URL風格 URL風格有如下三種,git

    • 駝峯式: userData
    • 中劃線式: user-data
    • 下劃線式: user_data

2.3. 版本管理

版本管理通常有兩種web

  • 位於url中的版本標識: http://example.com/api/v1/
  • 位於請求頭中的版本標識:Accept: application/vnd.redkavasyl+json; version=2.0

3. 請求

3.1. 最小化路徑嵌套

過深的路徑嵌套,會致使資源之間的關係顯得很是混亂。sql

最多3層嵌套爲佳,超過4層,則要考慮拆分接口。數據庫

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

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

3.2. URL參數優先原則

HTTP能夠在三個位置攜帶信息json

  1. URL 包括 path, 查詢字符串
  2. headers 包括請求頭、響應頭
  3. body 包括請求體、響應體
/call/:callId/hold
/call/callId?action=hold

非敏感參數放在URL中如下好處api

  1. 分析日誌會更方便 通常日誌系統都會打印出請求的url, 而不會打印body
  2. 減小請求體重傳輸的數據量

3.3. 資源名

  • 資源名建議使用複數形式
  • 建議使用字典中能查到單詞,不要隨意起名字
/calls/
/users/
/agents/

3.4. 請求方法

理解請求方法在http, sql, file的深層含義數組

層次 建立 查詢 修改 刪除
HTTP(web層) POST GET PUT DELETE
SQL(數據庫層) INSERT SELECT UPDATE DELETE
FILE(文件系統層) CREATE READ UPDATE DELETE

3.5. 查詢參數

3.5.1. 翻頁參數 page, pageSize

  • 一個系統,應當統一翻頁參數,例如都叫page, pageSize
  • 一個系統,應當統一塊兒始頁碼,要麼都從1開始,要麼都從0開始。建議從1開始。
  • pageSize應當有默認值最大值限制。
/agents?page=0&pageSize=20

3.5.2. 投影參數 fields

restful接口返回的數據格式每每都是寫死的,例如查詢agent信息,也許你要的只是agent姓名和年齡字段,可是每每獲取到一個agent的全部字段信息。瀏覽器

若是沒法自定義返回字段,那麼響應體每每不少無用的信息。安全

//bad
/agents?minAge=12

[{
  "name":"wdd",
  "age":"12",
  "address":"ss",
  "address":"ss",
  "org":{
    "children": {
      ""
      ....
    }
  }
  ....
},{
  "name":"ddw",
  "age":"12",
  "address":"ss",
  "org":{
    "children": {
      ""
      ....
    }
  }
  ....
}]
// better 只要獲取agent name 和 age字段
/agents?fields=name,age&minAge=12

[{
  "name":"wdd",
  "age":"12"
},{
  "name":"ddw",
  "age":"12"
}]

3.5.3. 控制器參數 actions

對於同一資源,當須要改變資源時,有時候僅僅使用PUT,沒法準確描述其動做。可是restful風格其實並不建議在path中使用動詞。服務器

因此對於此種狀況,建議增長action, 將信息放在查詢字符串中。

// bad
/pets/dogs/:id/actions/run
/pets/dogs/:id/actions/eat
/pets/dogs/:id/actions/bark

// better
/pets/dogs/:id?action=run
/pets/dogs/:id?action=eat
/pets/dogs/:id?action=bark

3.5.4. 其餘字段查詢參數

  • 字段查詢應當在符合業務需求的前提下,儘可能少的提供查詢維度。
  • 查詢參數的設計,應當儘量考慮要命中索引。這樣才能避免在數據量劇增時,致使查詢性能消耗極大。

3.6. 使用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"
  },
  ...
}

3.7. 安全限制

3.7.1 URL參數長度限制

what-is-the-maximum-length-of-a-url-in-different-browsers

若是url中有數組類型的查詢參數,則須要考慮數組支持最大長度。由於查詢參數最終將拼接到url中,而瀏覽器對url的長度是有限制的。爲了保證全部瀏覽器均可以兼容,最好將url的長度保持在低於2000字符。

若是請求體url太長,能夠給與414的狀態碼回覆

Browser Address bar document.location
Chrome 32779 >64k
Android 8192 >64k
Firefox >64k >64k
Safari >64k >64k
IE11 2047 5120
Edge 16 2047 10240

3.7.2 請求體body大小限制

太大的請求體,對服務端都是有壓力的,有可能形成服務崩潰。應當在接口設計階段,考慮到接口支持的最大請求體size,當請求體超過最大請求體時,應當直接回復413狀態碼。

4. 響應

4.1. 統一資源名稱

同一個資源名稱,在不一樣接口響應體中,應當具備相同的字段名。

例如訂單號,在A接口中叫作orderID, 在B接口中叫作orderNo。應當統一成一個字段名稱。

4.2. 提供Request-Id

一個請求異常了,若是沒有一個請求的惟一id, 那麼只能按照時間範圍去排查日誌,在日誌量大的狀況下,也是很難找到相應日誌的。

因此,建議在響應頭中加入一個字段Request-Id,建議用uuid。在將一個請求寫入日誌中時,同時寫入該請求的request-id。因爲每一個請求uuid都是惟一的,排查問題時會很是方便。

...
Request-Id: 3b99e3e0-7598-11e8-90be-95472fb3ecbd
date: Tue, 28 Aug 2018 13:07:53 GMT
expires: Thu, 19 Nov 1981 08:52:00 GMT
...

4.3. 合適的狀態碼

最經常使用的狀態碼

  • 1xx Informational

    • 100 Continue
    • 101 Switching Protocols
    • 102 Processing (WebDAV)
  • 2xx Success

    • 200 OK
    • 201 Created
    • 202 Accepted
    • 203 Non-Authoritative Information
    • 204 No Content
    • 205 Reset Content
    • 206 Partial Content
    • 207 Multi-Status (WebDAV)
    • 208 Already Reported (WebDAV)
    • 226 IM Used
  • 3xx Redirection

    • 300 Multiple Choices
    • 301 Moved Permanently
    • 302 Found
    • 303 See Other
    • 304 Not Modified
    • 305 Use Proxy
    • 306 (Unused)
    • 307 Temporary Redirect
    • 308 Permanent Redirect (experimental)
  • 4xx Client Error

    • 400 Bad Request
    • 401 Unauthorized
    • 402 Payment Required
    • 403 Forbidden
    • 404 Not Found
    • 405 Method Not Allowed
    • 406 Not Acceptable
    • 407 Proxy Authentication Required
    • 408 Request Timeout
    • 409 Conflict
    • 410 Gone
    • 411 Length Required
    • 412 Precondition Failed
    • 413 Request Entity Too Large
    • 414 Request-URI Too Long
    • 415 Unsupported Media Type
    • 416 Requested Range Not Satisfiable
    • 417 Expectation Failed
    • 418 I'm a teapot (RFC 2324)
    • 420 Enhance Your Calm (Twitter)
    • 422 Unprocessable Entity (WebDAV)
    • 423 Locked (WebDAV)
    • 424 Failed Dependency (WebDAV)
    • 425 Reserved for WebDAV
    • 426 Upgrade Required
    • 428 Precondition Required
    • 429 Too Many Requests
    • 431 Request Header Fields Too Large
    • 444 No Response (Nginx)
    • 449 Retry With (Microsoft)
    • 450 Blocked by Windows Parental Controls (Microsoft)
    • 451 Unavailable For Legal Reasons
    • 499 Client Closed Request (Nginx)
  • 5xx Server Error

    • 500 Internal Server Error
    • 501 Not Implemented
    • 502 Bad Gateway
    • 503 Service Unavailable
    • 504 Gateway Timeout
    • 505 HTTP Version Not Supported
    • 506 Variant Also Negotiates (Experimental)
    • 507 Insufficient Storage (WebDAV)
    • 508 Loop Detected (WebDAV)
    • 509 Bandwidth Limit Exceeded (Apache)
    • 510 Not Extended
    • 511 Network Authentication Required

4.4. 統一的錯誤模型

有時候錯誤的狀態碼沒法準確描述其錯誤類型,建議能夠提供惟一的枚舉類型的id來代表具體錯誤類型。

  • message 只是給開發者看的,不要把這個錯誤信息直接彈窗告訴最終用戶
  • 開發者能夠根據id字段,翻譯成具體的提示信息,告訴最終用戶。
{
  "id":      "rate_limit",
  "message": "Account reached its API rate limit.",
  "url":     "https://docs.service.com/rate-limits"
}

4.5. 老是提供資源的惟一id

[
  {
    id: '3b99e3e0-7598-11e8-90be-95472fb3ecbd',
    ...
  },
  {
    id: '3b99e3e0-7598-11e8-90be-95472fb3ecbd',
    ...
  }
]

4.6. 最小化響應體

  • 若是頁面上不展現該數據,就不要將該數據添加到響應體中
  • 不要出現沒法預知數組長度的數組嵌套,應當查分接口。並且數組嵌套每每難以分頁。
  • 避免數組中嵌套沒法預知長度的數組
  • 避免數組中嵌套沒法預知深度的樹,要避免沒法預知數據量大小的響應體

總之,要可以控制住響應體的大小。

舉個栗子,按天的查詢,關於詳情的數據應該放在另一個接口中查詢。

日期 通話時長 詳情
1號 40分鐘 [查看詳情]()
2號 40分鐘 [查看詳情]()
// bad
// GET /bills
// 在帳單的數組中,嵌套一個沒法預估長度的detail數組
// 若是detail數組過長,將會致使響應緩慢,太長的數據將會致使瀏覽器截斷json
[
  {
    id: '1'
    date: 1, 
    talkLength: 40, 
    detail: [
      {callingDevice: '888', ....},
      {callingDevice: '888', ....},
      {callingDevice: '888', ....},
      {callingDevice: '888', ....},
      {callingDevice: '888', ....},
    ]
  },
  {
    id: '2',
    date: 2, 
    talkLength: 80, 
    detail: [
      {callingDevice: '888', ....},
      {callingDevice: '888', ....},
      {callingDevice: '888', ....},
      {callingDevice: '888', ....},
      {callingDevice: '888', ....},
    ]
  }
]

// better
// GET /bills
// 將detail分離到專門查詢detail的接口中
[
  {
    date: 1, 
    talkLength: 40
  },
  {
    date: 2, 
    talkLength: 80
  }
]
// GET /bills/calls?day=2017-09-09&page=1&pageSize=20
[{
  {callingDevice: '888', ....},
  {callingDevice: '888', ....},
  {callingDevice: '888', ....},
  {callingDevice: '888', ....},
  {callingDevice: '888', ....},
}]

4.7. 不要在請求體中封裝狀態信息

200 ok 就能夠說明請求成功了,不必在請求體中再額外增長一個狀態字段。

GET 200 ok
{
  "status": "success",
  "agents": [{....}]
}

5. 接口文檔

5.1. 使用curl提供快速可執行例子

不要讓用戶看了半天文檔,也不知道怎麼傳參數。應當直截了當的給出一個可執行的例子。

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

5.2. 提供在線文檔和離線pdf文檔

  • 文檔網站 專門的在線文檔靜態網頁,適合對外接口文檔
  • swagger 文檔,適合對內接口文檔
  • markdown 文檔,方便,快捷,很容易轉化成pdf或者其餘格式
  • pdf文檔 適合與沒法訪問外網的開發者

6. 深刻REST

6.1. 理解無狀態

無狀態不是真正的無狀態,而是將狀態集中到專門的狀態管理服務中。

下圖所示的狀態是有狀態的服務,一旦一個請求到達某個服務,若是必需要求後續的請求,也必須到達該服務,那麼這個服務就是有狀態的。一旦該服務示例掛了,全部的狀態也就丟失了。

無狀態的服務要求實例不存儲狀態,把狀態交給專門的狀態服務器去管理。 集羣中的每一個實例可以徹底相等,一個實例掛了。後續的請求可以自動被分配到狀態正常的實例上去。

Jietu20180830-133446.jpg

7. 參考

相關文章
相關標籤/搜索