關注點分離(Separation of concerns,SOC)是對只與「特定概念、目標」(關注點)相關聯的軟件組成部分進行「標識、封裝和操縱」的能力,即標識、封裝和操縱關注點的能力。是處理複雜性的一個原則。因爲關注點混雜在一塊兒會致使複雜性大大增長,因此可以把不一樣的關注點分離開來,分別處理就是處理複雜性的一個原則,一種方法。-- 維基百科
URL風格 URL風格有如下三種,git
版本管理通常有兩種web
過深的路徑嵌套,會致使資源之間的關係顯得很是混亂。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}
HTTP能夠在三個位置攜帶信息json
/call/:callId/hold /call/callId?action=hold
非敏感參數放在URL中如下好處api
/calls/ /users/ /agents/
理解請求方法在http, sql, file的深層含義數組
層次 | 建立 | 查詢 | 修改 | 刪除 |
---|---|---|---|---|
HTTP(web層) | POST | GET | PUT | DELETE |
SQL(數據庫層) | INSERT | SELECT | UPDATE | DELETE |
FILE(文件系統層) | CREATE | READ | UPDATE | DELETE |
page
, pageSize
建議從1開始。
默認值
和最大值
限制。/agents?page=0&pageSize=20
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" }]
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
命中索引
。這樣才能避免在數據量劇增時,致使查詢性能消耗極大。$ 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" }, ... }
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 |
太大的請求體,對服務端都是有壓力的,有可能形成服務崩潰。應當在接口設計階段,考慮到接口支持的最大請求體size,當請求體超過最大請求體時,應當直接回復413
狀態碼。
同一個資源名稱,在不一樣接口響應體中,應當具備相同的字段名。
例如訂單號,在A接口中叫作orderID, 在B接口中叫作orderNo。應當統一成一個字段名稱。
一個請求異常了,若是沒有一個請求的惟一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 ...
最經常使用的狀態碼
1xx Informational
2xx Success
3xx Redirection
4xx Client Error
5xx Server Error
有時候錯誤的狀態碼沒法準確描述其錯誤類型,建議能夠提供惟一的枚舉類型的id來代表具體錯誤類型。
{ "id": "rate_limit", "message": "Account reached its API rate limit.", "url": "https://docs.service.com/rate-limits" }
[ { id: '3b99e3e0-7598-11e8-90be-95472fb3ecbd', ... }, { id: '3b99e3e0-7598-11e8-90be-95472fb3ecbd', ... } ]
避免數組中嵌套沒法預知長度的數組
避免數組中嵌套沒法預知深度的樹
,要避免沒法預知數據量大小的響應體總之,要可以控制住響應體的大小。
舉個栗子,按天的查詢,關於詳情的數據應該放在另一個接口中查詢。
日期 | 通話時長 | 詳情 |
---|---|---|
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', ....}, }]
200 ok 就能夠說明請求成功了,不必在請求體中再額外增長一個狀態字段。
GET 200 ok { "status": "success", "agents": [{....}] }
不要讓用戶看了半天文檔,也不知道怎麼傳參數。應當直截了當的給出一個可執行的例子。
$ 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" }, ... }
無狀態不是真正的無狀態,而是將狀態集中到專門的狀態管理服務中。
下圖所示的狀態是有狀態的服務,一旦一個請求到達某個服務,若是必需要求後續的請求,也必須到達該服務,那麼這個服務就是有狀態的。一旦該服務示例掛了,全部的狀態也就丟失了。
無狀態的服務要求實例不存儲狀態,把狀態交給專門的狀態服務器去管理。
集羣中的每一個實例可以徹底相等,一個實例掛了。後續的請求可以自動被分配到狀態正常的實例上去。