Restful API 架构与设计参考原则
一. 什么是REST
REST全稱是Representational State Transfer,中文意思是表述(編者注:通常譯為表征)性狀態轉移。 它首次出現在2000年Roy Fielding的博士論文中,Roy Fielding是HTTP規范的主要編寫者之一。 他在論文中提到:"我這篇文章的寫作目的,就是想在符合架構原理的前提下,理解和評估以網絡為基礎的應用軟件的架構設計,得到一個功能強、性能好、適宜通信的架構。REST指的是一組架構約束條件和原則。" 如果一個架構符合REST的約束條件和原則,我們就稱它為RESTful架構。
REST本身并沒有創造新的技術、組件或服務,而隱藏在RESTful背后的理念就是使用Web的現有特征和能力, 更好地使用現有Web標準中的一些準則和約束。雖然REST本身受Web技術的影響很深, 但是理論上REST架構風格并不是綁定在HTTP上,只不過目前HTTP是唯一與REST相關的實例。 所以我們這里描述的REST也是通過HTTP實現的REST。
?
什么是RESTful架構:
- 每一個URI代表一種資源;
- 客戶端和服務器之間,傳遞這種資源的某種表現層;
- 客戶端通過四個HTTP動詞(GET/POST/PUT/DELETE),對服務器端資源進行操作,實現”表現層狀態轉化”。
?
URI
要讓一個資源可以被識別,需要有個唯一標識,在Web中這個唯一標識就是URI(Uniform Resource Identifier)。 URI既可以看成是資源的地址,也可以看成是資源的名稱。如果某些信息沒有使用URI來表示,那它就不能算是一個資源, 只能算是資源的一些信息而已。URI的設計應該遵循可尋址性原則,具有自描述性,需要在形式上給人以直覺上的關聯。
?
二. HTTP動詞
?
使用GET、POST、PUT、DELETE這幾種請求模式
請求模式也可以說是動作、數據傳輸方式,通常我們在web中的form有GET、POST兩種,而在HTTP中,存在下發這幾種。
GET (選擇):從服務器上獲取一個具體的資源或者一個資源列表。?
POST (創建): 在服務器上創建一個新的資源。
PUT(更新):以整體的方式更新服務器上的一個資源。?
PATCH (更新):只更新服務器上一個資源的一個屬性。
DELETE(刪除):刪除服務器上的一個資源。
?
1.?使用名詞而不是動詞
| Resource | GET 讀 | POST 創建 | PUT 修改 | DELETE |
| /cars | 返回 cars集合 | 創建新的資源 | 批量更新cars | 刪除所有cars |
| /cars/711 | 返回特定的car | 該方法不允許(405) | 更新一個指定的資源 | 擅長指定資源 |
不要使用:
/getAllCars
/createNewCar
/deleteAllRedCars
?Get方法和查詢參數不應該涉及狀態改變
使用PUT, POST?和DELETE?方法?而不是?GET?方法來改變狀態,不要使用GET?進行狀態改變:
GET /users/711?activate?
GET /users/711/activate
?使用復數名詞
不要混淆名詞單數和復數,為了保持簡單,只對所有資源使用復數。
/cars 而不是 /car
/users 而不是 /user
/products 而不是 /product
/settings 而部署 /setting
?使用子資源表達關系
如果一個資源與另外一個資源有關系,使用子資源:
GET /cars/711/drivers/ 返回 car 711的所有司機
GET /cars/711/drivers/4 返回 car 711的4號司機
?
三. 為集合提供過濾 排序 選擇和分頁等功能
比如在數據過多, 需要對數據進行分頁請求的時候, 我們應該統一 API 請求參數. 常見的有這些.
- limit=10?指定返回記錄的數量
- offset=10?指定返回記錄的開始位置。
- page=2&per_page=100?指定第幾頁,以及每頁的記錄數。
- sortby=name&order=asc?指定返回結果按照哪個屬性排序,以及排序順序。
- animal_type_id=1?指定篩選條件
Filtering過濾:
使用唯一的查詢參數進行過濾:
GET /cars?color=red 返回紅色的cars
GET /cars?seats<=2 返回小于兩座位的cars集合
Sorting排序:
允許針對多個字段排序
GET /cars?sort=-manufactorer,+model
這是返回根據生產者降序和模型升序排列的car集合
Field selection
移動端能夠顯示其中一些字段,它們其實不需要一個資源的所有字段,給API消費者一個選擇字段的能力,這會降低網絡流量,提高API可用性。
GET /cars?fields=manufacturer,model,id,color
?
Paging分頁
使用 limit 和offset.實現分頁,缺省limit=20 和offset=0;
GET /cars?offset=10&limit=5
為了將總數發給客戶端,使用訂制的HTTP頭: X-Total-Count.
鏈接到下一頁或上一頁可以在HTTP頭的link規定,遵循Link規定:
Link: <https://blog.mwaysolutions.com/sample/api/v1/cars?offset=15&limit=5>; rel="next",
<https://blog.mwaysolutions.com/sample/api/v1/cars?offset=50&limit=3>; rel="last",
<https://blog.mwaysolutions.com/sample/api/v1/cars?offset=0&limit=5>; rel="first",
<https://blog.mwaysolutions.com/sample/api/v1/cars?offset=5&limit=5>; rel="prev",
?
四. 版本化你的API
API的開發直接關系了APP是否可以正常使用,如果原本運行正常的API,突然改動,那么之前使用這個API的APP可能無法正常運行。APP是不可能強迫用戶主動升級的,因此,通過API版本來解決這個問題。也就是說,API的多個版本是同時運行的,而且都要保證可以正常使用。
按照RESTful的規范,不同的版本也應該用相同的API URL,通過header信息來判斷版本,再調用不同版本的程序進行處理。但是這明顯會給開發帶來巨大的成本。使得API版本變得強制性,不要發布無版本的API,使用簡單數字,避免小數點如2.5.
域名
應該盡量將API部署在專用域名之下,如:https://api.example.com
也可以放在主域名下:https://example.org/api/
版本
不同的版本,用不同的URL來提供服務,在URL中通過v1、v2來區分版本號,比如v2.api.xxx.com/user的方式,或者http://api.domain.com/v2? 或者http://www.domain.com/api/v2 。
放入到頭信息的Accept中
制定版本并在版本之間平緩過渡對于設計和維護一套API是個巨大的挑戰。所以,最好在設計之初就使用一些方法來預防可能會遇到的問題。
為了避免API的變動導致用戶使用中產生意外結果或調用失敗,最好強制要求所有訪問都需要指定版本號。請避免提供默認版本號,一旦提供,日后想要修改它會相當困難。
最適合放置版本號的位置URL中,或者是頭信息(HTTP Headers)中在 Accept 段中使用自定義類型(content type)與其他元數據(metadata)一起提交。
提供 Request-Id
為每一個請求響應包含一個Request-Id字段,并使用UUID作為該值。通過在客戶端、服務器或任何支持服務上記錄該值,它能主我們提供一種機制來跟蹤、診斷和調試請求。
?
五. JSON數據類型
?
- Number:整數或浮點數
- String:字符串
- Boolean:true 或 false
- Array:數組包含在方括號[]中
- Object:對象包含在大括號{}中
- Null:空類型
所以,傳輸的數據類型不能超過這六種數據類型。以前,我們曾經試過傳輸Date類型,它會轉為類似于"2016年1月7日 09時17分42秒 GMT+08:00"這樣的字符串,這在轉換時會產生問題,不同的解析庫解析方式可能不同,有的可能會轉亂,有的可能直接異常了。要避免出錯,必須做特殊處理,自己手動去做解析。為了根除這種問題,最好的解決方案是用毫秒數或者字符串表示日期。
?
使用詳細的錯誤包裝錯誤:
?
{"errors": [{"userMessage": "Sorry, the requested resource does not exist","internalMessage": "No car found in the database","code": 34,"more info": "http://dev.mwaysolutions.com/blog/api/v1/errors/12345"}]}?
返回的數據結構
{code:0,message: "success",data: { key1: value1, key2: value2, ... } }?
- code: 返回碼,0表示成功,非0表示各種不同的錯誤
- message: 描述信息,成功時為"success",錯誤時則是錯誤信息
- data: 成功時返回的數據,類型為對象或數組
?
data字段只在請求成功時才會有數據返回的。數據類型限定為對象或數組,當請求需要的數據為單個對象時則傳回對象,當請求需要的數據是列表時,則為某個對象的數組。這里需要注意的就是,不要將data傳入字符串或數字,即使請求需要的數據只有一個,比如token,那返回的data應該為:
// 正確 data: { token: abcdedf }
六. 使用Http狀態碼處理錯誤
?
不同錯誤需要定義不同的返回碼,屬于客戶端的錯誤和服務端的錯誤也要區分,比如1XX表示客戶端的錯誤,2XX表示服務端的錯誤
如果你的API沒有錯誤處理是很難的,只是返回500和出錯堆棧不一定有用
Http狀態碼提供70個出錯,我們只要使用10個左右:
?
200 OK - [GET]:服務器成功返回用戶請求的數據,該操作是冪等的(Idempotent)。 201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數據成功。 202 Accepted - [*]:表示一個請求已經進入后臺排隊(異步任務) 204 NO CONTENT - [DELETE]:用戶刪除數據成功。 400 INVALID REQUEST - [POST/PUT/PATCH]:用戶發出的請求有錯誤,服務器沒有進行新建或修改數據的操作,該操作是冪等的。 401 Unauthorized - [*]:表示用戶沒有權限(令牌、用戶名、密碼錯誤)。 403 Forbidden - [*] 表示用戶得到授權(與401錯誤相對),但是訪問是被禁止的。 404 NOT FOUND - [*]:用戶發出的請求針對的是不存在的記錄,服務器沒有進行操作,該操作是冪等的。 406 Not Acceptable - [GET]:用戶請求的格式不可得(比如用戶請求JSON格式,但是只有XML格式)。 410 Gone -[GET]:用戶請求的資源被永久刪除,且不會再得到的。 422 Unprocesable entity - [POST/PUT/PATCH] 當創建一個對象時,發生一個驗證錯誤。 500 INTERNAL SERVER ERROR - [*]:服務器發生錯誤,用戶將無法判斷發出的請求是否成功。?
七. 允許覆蓋HTTP方法
?
一些代理只支持POST?和?GET方法,?為了使用這些有限方法支持RESTful API,需要一種辦法覆蓋http原來的方法。
使用訂制的HTTP頭?X-HTTP-Method-Override?來覆蓋POST 方法.
?
?
參考:
總結
以上是生活随笔為你收集整理的Restful API 架构与设计参考原则的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2021年济民制药还能持有吗 2021年
- 下一篇: [Golang] 第三方包应该如何安装-