梳理REST API的設計原則

什麼是 REST ?


REST架構風格描述了六個約束。應用於體系結構的這些約束最初由Roy Fielding在他的博士論文中提出(參見 https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm)該文是RESTful-style的基礎 。

這六個約束是:

 

Uniform Interface (統一接口)
統一接口約束定義了客戶端和服務器之間的接口。它簡化並解耦了架構,使每個部件都能獨立演變。 統一接口的四個指導原則是:

 

Resource-Based (基於資源的)

使用URI作爲資源標識符在請求中標識各個資源。 資源本身在概念上與返回給客戶端的表示(representations)分開。 例如,服務器不直接發送其數據庫內容,而是發送一些表示某些數據庫記錄的HTML,XML或JSON,例如,用芬蘭語表示並以UTF-8編碼,具體取決於請求的詳細信息和服務器實現。

 

Manipulation of Resources Through Representations
當客戶端持有資源的表示(包括附加的任何元數據)時,它有足夠的信息來修改或刪除服務器上的資源,前提是它有權這樣做。

 

Self-descriptive Messages (自描述信息)
每條消息都包含足夠的信息來描述如何處理該消息。 例如,要調用的解析器可以由Internet媒體類型 media type(以前稱爲MIME類型)指定。 響應還明確指出了它們是否可以被緩存。

 

Hypermedia as the Engine of Application State (HATEOAS) (超媒體作爲應用程序狀態引擎)
客戶端通過body 內容,查詢字符串參數,請求header和請求的URI(資源名稱)來提供狀態。 服務通過正文內容,響應代碼和響應標頭向客戶提供狀態。 這在技術上被稱爲超媒體(或超文本中的超鏈接)。

 

除了上面的描述之外,HATEOS還意味着,在必要時,鏈接包含在返回的正文(或標題)中,以提供用於檢索對象本身或相關對象的URI。 我們稍後會詳細討論這個問題。

 

任何REST服務必須提供的統一接口是其設計的基礎

 

Stateless (無狀態的)
由於REST是REpresentational State Transfer的首字母縮寫,statelessness (無狀態)是關鍵。 這意味着要處理請求的狀態必須包含在請求本身中,無論是作爲URI,查詢字符串參數,正文還是標題的一部分。 URI唯一標識資源,body包含該資源的狀態(或狀態變化)。 然後,在服務器進行處理之後,通過headers,狀態和響應主體將適當的狀態或重要狀態的片斷傳送回客戶端。

 

我們大多數已經在業界工作了一段時間的人習慣於在container(不是docker中的) 內編程,這爲我們提供了“session”的概念,它在多個HTTP請求中維護狀態。 在REST中,客戶端必須包含服務器的所有信息以完成請求,如果該狀態必須跨越多個請求,則根據需要重新發送狀態。 無狀態可以實現更高的可伸縮性,因爲服務器不必維護,更新或傳遞該會話狀態。 此外,負載均衡器不必擔心無狀態系統的會話親和性。

 

那麼狀態(state)和資源(resource)之間的區別是什麼? 狀態或應用程序狀態是服務器關心的,以滿足當前會話或請求所需的請求數據。 資源或資源狀態是定義資源表示的數據 - 例如,存儲在數據庫中的數據。 將應用程序狀態視爲可能因客戶端和每個請求而異的數據。 另一方面,資源狀態在請求它的每個客戶端都是不變的。

 

Cacheable
與萬維網一樣,客戶端可以緩存響應。 因此,響應必須隱式或顯式地將自身定義爲可緩存或不可緩存,以防止客戶端重用陳舊或不適當的數據以影響進一步的請求。 管理良好的緩存部分或完全消除了一些客戶端 - 服務器交互,進一步提高了可伸縮性和性能。

 

Client-Server
統一接口將客戶端與服務器分開。 這種關注點分離意味着,例如,客戶端不關心數據存儲,數據存儲仍保留在每個服務器的內部,從而提高了客戶端代碼的可移植性。 服務器不關心用戶界面或用戶狀態,因此服務器可以更簡單,更具可伸縮性。 只要不改變接口,服務器和客戶端也可以獨立替換和開發。

 

Layered System (分層系統)
客戶端通常無法判斷它是直接連接到終端服務器,還是中間服務器。 中間服務器可以通過啓用負載平衡和提供共享緩存來提高系統可伸縮性。 Layers 也可以實施安全策略。

 

Code on Demand (optional)
服務器能夠通過向客戶端傳輸可以執行的邏輯來臨時擴展或自定義客戶端的功能。 這樣的示例可以包括編譯的組件,例如Java applet和客戶端腳本,例如JavaScript。

 

遵守這些約束,從而符合REST架構風格,將使任何類型的分佈式超媒體系統具有理想的緊急(emergent)屬性,例如性能,可伸縮性,簡單性,可修改性,可見性,可移植性和可靠性。

 

注意:REST架構的唯一可選約束是code on demand。 如果服務違反任何其他約束,則嚴格來講不能將其稱爲RESTful。

 

REST API Quick Tips
無論技術上是不是REST(根據前面提到的六個約束條件),這裏有一些推薦的類似REST的概念。 這六個快速提示將帶來更好,更實用的服務。

 

使用HTTP動詞使你的請求帶有含意
API使用者能夠發送GET,POST,PUT和DELETE請求,這極大地增強了給定請求的清晰度。

 

通常,四個主要的HTTP動詞使用如下:

 

GET :讀取特定資源(通過標識符)或資源集合。

 

PUT :更新特定資源(通過標識符)或資源集合。 如果資源標識符是事先已知的,也可以用於創建特定資源。

 

DELETE :通過標識符刪除指定資源。

 

POST :創建一個新資源。 對於不適合其他類別的操作,也是一個萬能的動詞。

 

注意 :GET請求不得更改任何底層資源數據。 但可能會更新測量和跟蹤數據,但URI標識的資源數據不應更改。

 

提供合理的資源名稱
製作出色的API需要80%的藝術和20%的科學。 創建表示合理資源的URL層次結構是藝術部分。 擁有合理的資源名稱(只是URL路徑,例如/customers/12345/orders)可以提高給定請求的清晰度。

 

適當的資源名稱爲服務請求提供上下文,從而提高API的可理解性。 通過URI名稱對資源進行分層查看,爲消費者提供友好,易於理解的資源層次結構,以便在其應用程序中使用。

 

以下是一些URL路徑(資源名稱)設計的規則:

在你的網址中使用標識符,而不是在查詢字符串中使用。 使用URL查詢字符串參數非常適合過濾,但不適用於資源名稱。
Good: /users/12345
Poor: /api?type=user&id=23


利用URL的分層特性來表示結構


爲你的客戶而不是數據設計


資源名稱應爲名詞

避免使用動詞作爲資源名稱,以提高清晰度。 使用HTTP methods 指定請求的動詞部分。


在URL段中使用複數形式,使用集合使API URI在所有HTTP方法中保持一致。
Recommended: /customers/33245/orders/8769/lineitems/1
Not: /customer/33245/order/8769/lineitem/1


避免在URL中使用集合詞

例如'customer_list'作爲資源。 使用複數來隱含表示集合(例如,customers 代替customer_list)。


在URL段中使用小寫,用下劃線('_')或連字符(' - ')分隔單詞

有些服務器會忽略大小寫,所以最好清楚。


保持URL儘可能短,儘可能少的分段

 

使用HTTP響應代碼指示狀態


響應狀態代碼是HTTP規範的一部分。 它們中有很多可以gggg解決最常見的情況。 本着使RESTful服務包含HTTP規範的精神,我們的Web API應該返回相關的HTTP狀態代碼。 例如,當成功創建資源時(例如,來自POST請求),API應該返回HTTP狀態代碼201.這裏有常用的HTTP狀態代碼列表,其列出了每個的詳細描述。

 

十大HTTP響應狀態代碼的建議用法如下:

 

200 OK :通用成功狀態代碼。 這是最常見的代碼。 用於表示成功。

201 CREATED :成功創建(通過POST或PUT)。 將Location header設置爲包含指向新創建的資源的鏈接(在POST上)。 響應body 內容可能存在也可能不存在。

204 NO CONTENT :表示成功,但響應body中沒有任何內容,通常用於DELETE和PUT操作。

400 BAD REQUEST :完成請求時的一般錯誤會導致無效狀態。 例如域驗證錯誤,缺少數據等。

401 UNAUTHORIZED :丟失或無效的身份驗證令牌。

403 FORBIDDEN :當用戶未被授權執行操作或資源由於某種原因(例如時間限 制等)不可用時的錯誤代碼。

404 NOT FOUND :在找不到請求的資源時使用,不管是否不存在,或者是否是401或403,出於安全原因,服務需要屏蔽。

405 METHOD NOT ALLOWED :表示請求的URL存在,但請求的HTTP方法不對。 例如,POST /users/12345,其API不支持以這種方式創建資源(使用提供的ID)。 返回405時必須設置Allow header表明支持的HTTP方法。 例如:"Allow: GET, PUT, DELETE"

409 CONFLICT :請求導致資源衝突時。 重複條目,例如嘗試創建具有相同信息的兩個客戶,以及在不支持級聯刪除時刪除根對象。

500 INTERNAL SERVER ERROR :永遠不要故意返回該狀態碼。 服務器端拋出異常時應使用catch-all捕捉。 僅將此用於客戶端無法解決的錯誤(即服務器錯誤,client做啥也沒有用,請聯繫後端人員解決)。

 

提供JSON和XML
一般只支持JSON就可以了,除非是高度標準化和受監管的行業,需要XML。 模式驗證和命名空間,並提供JSON和XML,是非常高的。 理想情況下,讓消費者使用HTTP Accept header在格式之間切換,或者只是在URL上將.xml的擴展名更改爲.json。

 

請注意,一旦我們開始討論XML支持,我們就會開始討論用於驗證,命名空間等的模式。除非你的行業需要,否則請儘量避免支持所有這些複雜性。 JSON旨在簡化,簡潔和實用。 如果可以的話,讓你的XML看起來更簡潔。

 

換句話說,使返回的XML更像JSON - 簡單易讀,不存在架構和命名空間細節,只有數據和鏈接。 如果它最終比這更復雜,那麼XML的成本將是驚人的。 根據我的經驗,過去幾年沒有人使用過XML響應,成本太昂貴了。

 

請注意,JSON-Schema提供了架構式驗證功能。

 

創建細粒度資源
在開始時,最好創建模仿系統的底層應用程序域模型或數據庫體系結構的API。 最終,聚合那些需要利用多個底層資源的服務來減少通信量。 但是,從單個資源創建更大的資源比從更大的聚合創建細粒度或單個資源要容易得多。 讓自己輕鬆自如,從易於定義的小型資源開始,爲這些資源提供CRUD功能。 你可以之後創建這些方面的用例,減少通信。

 

考慮連接性
REST的原則之一是連通性 - 通過超媒體鏈接(搜索HATEOAS)。 雖然沒有超鏈接,服務仍然有用,但在響應中返回鏈接時,API會變得更具自我描述性和可發現性。 至少,“自我描述”的鏈接引用會告訴客戶端如何檢索數據。 此外,利用HTTP Location header 包含通過POST(或PUT)創建資源的鏈接。 對於在支持分頁的響應中返回的集合,“first”,“last”,“next”和“prev”鏈接至少是非常有用的。

 

關於鏈接格式,有很多。 HTTP Web鏈接規範(RFC5988)解釋瞭如下鏈接:

 

鏈接是由Internationalised Resource Identifiers (IRIs) [RFC3987]標識的兩個資源之間的類型連接,包括:

上下文IRI,
鏈接關係類型
目標IRI,和
可選的目標屬性。
鏈接可以被視爲“{context IRI}在{target IRI}具有{relation type}資源的形式的聲明,其具有{target attributes}。”

 

至少,按照規範中的建議放置HTTP鏈接頭中的鏈接,或者在JSON表示中包含此HTTP鏈接樣式的JSON表示(例如Atom樣式鏈接,請參閱:RFC4287)。 之後,隨着REST API變得更加成熟,你可以在更復雜的鏈接樣式中進行分層,例如HAL + JSON,Siren,Collection + JSON 或 JSON-LD等。

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