1. 程式人生 > >梳理REST API的設計原則

梳理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等。