淺談RESTful API接口設計標準及規範,及Django rest_framework使用

前言

網絡應用程序,分爲前端和後端兩個部分。當前的發展趨勢,就是前端設備層出不窮(手機、平板、桌面電腦、其他專用設備…)。因此,必須有一種統一的機制,方便不同的前端設備與後端進行通信。這導致API構架的流行,甚至出現"APIFirst"的設計思想。RESTful API是目前比較成熟的一套互聯網應用程序的API設計理論。

REST(Representational State Transfer)表述性狀態轉換,REST指的是一組架構約束條件和原則。 如果一個架構符合REST的約束條件和原則,我們就稱它爲RESTful架構。REST本身並沒有創造新的技術、組件或服務,而隱藏在RESTful背後的理念就是使用Web的現有特徵和能力, 更好地使用現有Web標準中的一些準則和約束。雖然REST本身受Web技術的影響很深, 但是理論上REST架構風格並不是綁定在HTTP上,只不過目前HTTP是唯一與REST相關的實例

一.URI規範

不用大寫;
用中槓-不用下槓_;
參數列表要encode;
URI中的名詞表示資源集合,使用複數形式。

二.資源集合 ,單個資源
URI表示資源的兩種方式:資源集合、單個資源。

資源集合:

/zoos //所有動物園
/zoos/1/animals //id爲1的動物園中的所有動物
單個資源:

/zoos/1 //id爲1的動物園
/zoos/1;2;3 //id爲1,2,3的動物園

三.避免層級過深的URI
/在url中表達層級,用於按實體關聯關係進行對象導航,一般根據id導航。

過深的導航容易導致url膨脹,不易維護,如 GET /zoos/1/areas/3/animals/4,儘量使用查詢參數代替路徑中的實體導航,如GET /animals?zoo=1&area=3;

四.對Composite資源的訪問
服務器端的組合實體必須在uri中通過父實體的id導航訪問。

組合實體不是first-class的實體,它的生命週期完全依賴父實體,無法獨立存在,在實現上通常是對數據庫表中某些列的抽象,不直接對應表,也無id。一個常見的例子是 User — Address,Address是對User表中zipCode/country/city三個字段的簡單抽象,無法獨立於User存在。必須通過User索引到Address:GET /user/1/addresses

五. Request(請求)
HTTP方法
通過標準HTTP方法對資源CRUD:

GET:查詢

GET /zoos
GET /zoos/1
GET /zoos/1/employees

POST:創建單個資源。POST一般向“資源集合”型uri發起

POST /animals //新增動物
POST /zoos/1/employees //爲id爲1的動物園僱傭員工

PUT:更新單個資源(全量),客戶端提供完整的更新後的資源。與之對應的是 PATCH,PATCH 負責部分更新,客戶端提供要更新的那些字段。PUT/PATCH一般向“單個資源”型uri發起

PUT /animals/1
PUT /zoos/1

DELETE:刪除

DELETE /zoos/1/employees/2
DELETE /zoos/1/employees/2;4;5
DELETE /zoos/1/animals //刪除id爲1的動物園內的所有動物

HEAD / OPTION 用的不多,就不多過多贅述。

六.安全性和冪等性
安全性:不會改變資源狀態,可以理解爲只讀的;
冪等性:執行1次和執行N次,對資源狀態改變的效果是等價的。

請求方法  安全性 冪等性
GET
POST × ×
PUT ×
DELETE ×



 

 

 

 

安全性和冪等性均不保證反覆請求能拿到相同的response。以 DELETE 爲例,第一次DELETE返回200表示刪除成功,第二次返回404提示資源不存在,這是允許的。

七. 複雜查詢
查詢可以捎帶以下參數:

操作 示例 備註
過濾條件 ?type=1&age=16 允許一定的uri冗餘,如/zoos/1與/zoos?id=1
排序 ?sort=age,desc  
投影 ?whitelist=id,name,email    
分頁 ?limit=10&offset=3    

 

 

 

 

 

 

 

八.Bookmarker(書籤)
經常使用的、複雜的查詢標籤化,降低維護成本。

如:
GET /trades?status=closed&sort=created,desc
快捷方式:
GET /trades#recently-closed
或者
GET /trades/recently-closed

九.Format(request格式)
只用以下常見的3種body format:

1.Content-Type: application/json

POST /v1/animal HTTP/1.1
Host: api.example.org
Accept: application/json
Content-Type: application/json
Content-Length: 24

{ 
"name": "Gir",
"animalType": "12"
}
2.Content-Type: application/x-www-form-urlencoded (瀏覽器POST表單用的格式)

POST /login HTTP/1.1
Host: example.com
Content-Length: 31
Accept: text/html
Content-Type: application/x-www-form-urlencoded

username=root&password=Zion0101
Content-Type: multipart/form-data; boundary=—-RANDOM_jDMUxq4Ot5 (表單有文件上傳時的格式)

Content Negotiation
資源可以有多種表示方式,如json、xml、pdf、excel等等,客戶端可以指定自己期望的格式,通常有兩種方式:

1.http header Accept:

Accept:application/xml;q=0.6,application/atom+xml;q=1.0
1.爲各項格式的偏好程度

2.url中加文件後綴:/zoo/1.json

十. Response(響應)
不要過度包裝: 
response 的 body 直接就是數據,不要做多餘的包裝。錯誤示例:

{
"success":true,
"data":{"id":1,"name":"xiaotuan"},
}

各HTTP方法成功處理後的數據格式:

方法 response 格式
GET 單個對象、集合
POST 新增成功的對象
PUT/PATCH 更新成功的對象
DELETE None
1 json格式的約定:
2 
3 時間用長整形(毫秒數),客戶端自己按需解析(moment.js)
4 不傳null字段
5 分頁response
6 {
7 "paging":{"limit":10,"offset":0,"total":729},
8 "data":[{},{},{}...]
9 }

十一. 錯誤處理
不要發生了錯誤但給2xx響應,客戶端可能會緩存成功的http請求;
正確設置http狀態碼,不要自定義;
Response body 提供 1) 錯誤的代碼(日誌/問題追查);2) 錯誤的描述文本(展示給用戶)。

服務器端一般用異常表示 RESTful API 的錯誤。API 可能拋出兩類異常:業務異常和非業務異常。業務異常由自己的業務代碼拋出,表示一個用例的前置條件不滿足、業務規則衝突等,比如參數校驗不通過、權限校驗失敗。非業務類異常表示不在預期內的問題,通常由類庫、框架拋出,或由於自己的代碼邏輯錯誤導致,比如數據庫連接失敗、空指針異常、除0錯誤等等。

業務類異常必須提供2種信息:

1 如果拋出該類異常,HTTP 響應狀態碼應該設成什麼;
2 異常的文本描述;

在Controller層使用統一的異常攔截器:

1 設置 HTTP 響應狀態碼:對業務類異常,用它指定的 HTTP code;對非業務類異常,統一500;
2 Response Body 的錯誤碼:異常類名
3 Response Body 的錯誤描述:對業務類異常,用它指定的錯誤文本;對非業務類異常,線上可以統一文案如“服務器端錯誤,請稍後再試”,開發或測試環境中用異常的 stacktrace,服務器端提供該行爲的開關。

常用的http狀態碼及使用場景:

狀態碼 業務場景
400 bad request 常用在參數校驗
401 unauthorized 未經驗證的用戶,常見於未登錄。如果經過驗證後依然沒權限,應該 403(即 authentication 和 authorization 的區別)
403 forbidden 無權限
404 not found 資源不存在
500 internal server error 非業務類異常
503 service unavaliable 由容器拋出,自己的代碼不要拋這個異常

十二. 服務型資源
除了資源簡單的CRUD,服務器端經常還會提供其他服務,這些服務無法直接用上面提到的URI映射。如:

按關鍵字搜索;
1.計算地球上兩點間的距離;
2.批量向用戶推送消息
3.可以把這些服務看成資源,計算的結果是資源的presentation,按服務屬性選擇合適的HTTP方法。

例:

1 GET /search?q=filter?category=file 搜索
2 GET /distance-calc?lats=47.480&lngs=-122.389&late=37.108&lnge=-122.448
3 POST /batch-publish-msg
4 [{"from":0,"to":1,"text":"abc"},{},{}...]

十三. 異步任務
對耗時的異步任務,服務器端接受客戶端傳遞的參數後,應返回創建成功的任務資源,其中包含了任務的執行狀態。客戶端可以輪訓該任務獲得最新的執行進度。

1 提交任務:
2 POST /batch-publish-msg
3 [{"from":0,"to":1,"text":"abc"},{},{}...]
4 
5 返回:
6 {"taskId":3,"createBy":"Anonymous","status":"running"}
7 
8 GET /task/3
9 {"taskId":3,"createBy":"Anonymous","status":"success"}

如果任務的執行狀態包括較多信息,可以把“執行狀態”抽象成組合資源,客戶端查詢該狀態資源瞭解任務的執行情況。

1 提交任務:
2 POST /batch-publish-msg
3 [{"from":0,"to":1,"text":"abc"},{},{}...]
4 
5 返回:
6 {"taskId":3,"createBy":"Anonymous"}
7 
8 GET /task/3/status
9 {"progress":"50%","total":18,"success":8,"fail":1}

十四. API的演進
版本
常見的三種方式:

1 1.在uri中放版本信息:GET /v1/users/1
2 2.Accept Header:Accept: application/json+v1
3 3.自定義 Header:X-Api-Version: 1
4 用第一種,雖然沒有那麼優雅,但最明顯最方便。

URI失效
隨着系統發展,總有一些API失效或者遷移,對失效的API,返回404 not found 或 410 gone;對遷移的API,返回 301 重定向。
由於篇幅已經較大了,Django rest_framework使用 點擊猛戳

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章