最近經常跟朋友討論到什麼是RESTful API,從不同人的身上得到了不同的解釋。
有些人說,使用了Web API就是RESTful API了
也有些人說,RESTful API就是就是簡單的CRUD,URL不重要
再細一點會有人討論Session是否可以存在、利用回傳的JSON資料定義狀態,亦或是使用HTTP Status Code?
針對這些疑問,我找了一些資料分享出來,一起來討論這些問題,到底哪些才真正符合RESTful API的風格。
官方版:什麼是REST?
民間版:理解RESTful架构
REST架構風格描述了六個約束。這些適用於架構的約束最初由Roy Fielding在他的博士論文中傳達(參見http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm),並定義了RESTful-樣式。
六個約束是:
- 統一的界面
統一的接口約束定義了客戶端和服務器之間的接口。它簡化和分離了架構,使每個部分獨立演變。統一接口的四個指導原則是:
資源型
在使用URI作為資源標識符的請求中標識各個資源。資源本身在概念上與返回給客戶端的表示分離。例如,服務器不會發送它的數據庫,而是發送一些HTML,XML或JSON來表示一些數據庫記錄,例如以芬蘭語表示並以UTF-8編碼,具體取決於請求和服務器實現的細節。
通過表示操縱資源
當客戶端持有資源的表示形式(包括附加的任何元數據)時,它擁有足夠的信息來修改或刪除服務器上的資源,前提是它有權這樣做。
自描述性消息
每條消息都包含足夠的信息來描述如何處理消息。例如,要調用哪個解析器可以通過Internet媒體類型(以前稱為MIME類型)指定。響應還明確指出了它們的緩存能力。
超媒體作為應用程序狀態引擎(HATEOAS)
客戶通過正文內容,查詢字符串參數,請求標頭和請求的URI(資源名稱)傳遞狀態。服務通過主體內容,響應代碼和響應頭向客戶端傳遞狀態。這在技術上被稱為超媒體(或超文本中的超鏈接)。
除上述描述外,HATEOS還意味著必要時,鏈接包含在返回的主體(或多個標題)中,以提供用於檢索對象本身或相關對象的URI。稍後我們會更詳細地討論這一點。
任何REST服務必須提供的統一界面是其設計的基礎。
- 無狀態
由於REST是REpresentational State Transfer的縮寫,無狀態是關鍵。實質上,這意味著處理請求的必要狀態包含在請求本身內,無論是作為URI,查詢字符串參數,正文還是標題的一部分。URI唯一標識資源,而主體包含該資源的狀態(或狀態更改)。然後,在服務器完成它的處理後,合適的狀態或重要的狀態塊通過標題,狀態和響應主體傳回客戶端。
我們大多數已經在業界工作一段時間的人習慣於在一個容器內進行編程,這個容器為我們提供了“會話”的概念,它在多個HTTP請求中保持狀態。在REST中,客戶端必須包含服務器的所有信息以完成請求,如果該狀態必須跨越多個請求,則必要時重新發送狀態。由於服務器無需維護,更新或傳達會話狀態,因此無狀態可實現更高的可伸縮性。另外,負載平衡器不必擔心無狀態系統的會話關聯性。
那麼國家和資源之間有什麼區別呢?狀態或應用程序狀態是服務器正在關心的用於完成當前會話或請求所需的數據的請求。資源或資源狀態是定義資源表示的數據,例如存儲在數據庫中的數據。考慮應用程序狀態是可能因客戶端和每個請求而異的數據。另一方面,資源狀態在每個請求它的客戶端都是不變的。
曾經有過一個Web應用程序的後退按鈕問題,它在某個特定時間發布了AWOL,因為它希望您按特定順序執行某些操作?那是因為它違反了無國籍原則。有些情況下不遵守無狀態原則,例如三段式OAuth,API調用速率限制等。但是,盡一切努力確保應用程序狀態不會跨越您的服務的多個請求。
- 可緩存
就互聯網而言,客戶端可以緩存響應。因此,響應必須隱含地或明確地將其自身定義為可緩存或不可以,以防止客戶端響應於進一步的請求而重新使用陳舊或不適當的數據。管理良好的緩存部分或完全消除了一些客戶端 - 服務器交互,進一步提高了可伸縮性和性能。
- 客戶端服務器
統一的接口將客戶端與服務器分開。這種關注的分離意味著,例如,客戶端不關心數據存儲,而數據存儲仍然是每個服務器的內部存儲,因此客戶端代碼的可移植性得到了提高。服務器不關心用戶界面或用戶狀態,因此服務器可以更簡單,更具可擴展性。只要接口沒有改變,服務器和客戶端也可以獨立地進行替換和開發。
- 分層系統
客戶通常無法判斷它是直接連接到最終服務器還是直接連接到中間人。中介服務器可以通過啟用負載平衡和提供共享緩存來提高系統可擴展性。層也可能強制執行安全策略。
- 按需代碼(可選)
服務器能夠通過傳遞邏輯來臨時擴展或定制客戶端的功能,以實現客戶端的功能。這樣的例子可能包括編譯的組件,例如Java小應用程序和諸如JavaScript的客戶端腳本。
遵守這些約束條件,從而符合REST架構風格,將使任何類型的分佈式超媒體系統具有理想的緊急屬性,如性能,可伸縮性,簡單性,可修改性,可見性,可移植性和可靠性。
注意: REST體系結構的唯一可選約束是按需代碼。如果服務違反了其他約束條件,則不能嚴格將其稱為RESTful。
官方版:REST API 快速提示
民間版:RESTful API 设计指南
不管是在技術上還是 rest (根據前面提到的六限制), 這裡有一些建議的 rest 概念。這六個快速提示將導致更好、更有用的服務。
使用 HTTP 謂詞使您的請求具有某種意義
API 使用者可以發送 "獲取"、"張貼"、"放置" 和 "刪除" 謂詞, 這大大提高了給定請求的清晰度。
通常, 四主要 HTTP 謂詞如下所示:
- GET
- 讀取特定資源 (按識別碼) 或資源集合。
- PUT
- 更新特定資源 (按識別碼) 或資源集合。如果資源識別碼是已知的, 也可用於創建特定資源。
- DELETE
- 通過識別碼刪除/刪除特定資源。
- POST
- 創建新資源。也是一個不適合其他類別的操作的 catch 謂詞。
注意
獲取請求不能更改任何基礎資源資料。測量和跟蹤哪些更新資料可能仍會發生, 但 URI 標識的資源資料不應更改。
提供合理的資源名稱
製作一個偉大的 API 是80% 藝術和20% 科學。創建代表合理資源的 URL 層次結構是藝術部分。具有合理的資源名稱 (只是 URL 路徑 (如 customers/12345/orders) 可以提高給定請求的清晰度。
適當的資源名稱為服務請求提供上下文, 從而增加了 API 的易懂。資源通過它們的 URI 名稱分層查看, 為消費者提供一個友好的、易於理解的資源層次結構, 以利用它們的應用程式。
下面是一些用於 URL 路徑 (資源名稱) 設計的快速命中規則:
- 在 url 中使用識別碼而不是查詢字串。使用 URL 查詢字串參數非常適合篩選, 但不能用於資源名稱。
- 建議: /users/12345
- 不建議: /api?type=user&id=23
- 利用 URL 的分層特性來表示結構。
- 為您的客戶設計, 而不是為您的資料。
- 資源名稱應為名詞。避免將動詞作為資源名稱, 以提高清晰度。使用 HTTP 方法指定請求的動詞部分。
- 使用 URL 段中的複數, 可以使用集合隱喻使 API uri 在所有 HTTP 方法中保持一致。
- 建議: /customers/33245/orders/8769/lineitems/1
- 不建議: /customer/33245/order/8769/lineitem/1
- 避免在 url 中使用集合措辭。例如 "customer_list" 作為資源。使用複數表示集合隱喻 (如客戶與 customer_list)。
- 在 URL 段中使用小寫, 用底線 ('_') 或連字號 ('-') 分隔單詞。有些伺服器忽略大小寫, 所以最好是清除。
- 保持網址盡可能短, 並盡可能少的部分是有意義的。
使用 HTTP 回應代碼指示狀態
回應狀態碼是 HTTP 規範的一部分。有相當多的人來處理最常見的情況。本著讓我們的 rest 服務接受 HTTP 規範的精神, 我們的 Web api 應該返回相關的 HTTP 狀態碼。例如, 在成功創建資源 (例如從 POST 請求中) 時, API 應返回 HTTP 狀態碼201。有效的HTTP 狀態碼清單可用此處列出每個的詳細說明。
"前 10" HTTP 回應狀態碼的建議用法如下:
- 200 OK
- 常規成功狀態碼。這是最常見的代碼。用來表示成功。
- 201 CREATED(創建)
- 成功創建 (通過 POST 或投入)。設置位置標題以包含指向新創建的資源的連結 (開機自檢)。回應正文內容可能存在也可能不存在。
- 204 NO CONTENT(無內容)
- 表示成功, 但回應正文中沒有任何東西, 通常用於刪除和放置操作。
- 400 BAD REQUEST(錯誤請求)
- 當滿足請求時的常規錯誤將導致無效狀態。域驗證錯誤、缺失資料等是一些例子。
- 401 UNAUTHORIZED(未經授權)
- 缺少或不正確身份驗證權杖的錯誤代碼回應。
- 403 FORBIDDEN(禁止)
- 當使用者未被授權執行操作或資源因某種原因而無法接通 (例如, 時間限制等) 時的錯誤代碼。
- 404 NOT FOUND(未找到)
- 在找不到請求的資源時使用, 無論它是否存在, 或者是否存在401或 403, 出於安全原因, 服務要遮罩。
- 405 METHOD NOT ALLOWED(方法不允許)
- 用於指示請求的 URL 存在, 但請求的 HTTP 方法不適用。例如, 發佈users/12345後, API 不支援以這種方式 (提供的 ID) 創建資源。在返回405以指示支援的 HTTP 方法時, 必須設置允許 HTTP 標頭。在前面的情況下, 頁眉看起來像 "允許: 獲取, 放置, 刪除"
- 409 CONFLICT(衝突)
- 只要滿足請求, 就會導致資源衝突。重複的條目 (如嘗試創建兩個具有相同資訊的客戶) 不支援串聯刪除時刪除根物件, 這是幾個示例。
- 500 INTERNAL SERVER ERROR(內部伺服器錯誤)
- 不要故意把這個還給我。伺服器端引發異常時的常規 catch 錯誤。僅適用于消費者無法從其端位址進行處理的錯誤。
同時提供 JSON 和 XML
青睞 json 支援, 除非您處於一個要求 XML、架構驗證和命名空間的高度標準化和受管理的行業, 並且提供 json 和 XML, 除非成本驚人。理想情況下, 讓消費者使用 HTTP 接受標頭在格式之間切換, 或者只在 URL 上更改從. xml 到. json 的擴展。
請注意, 一旦我們開始討論 XML 支援, 我們就開始討論驗證、命名空間等模式。除非您的行業需要, 否則應避免最初支援所有的複雜性, 如果有的話。JSON 的設計簡單, 簡潔, 功能。如果可以的話, 讓你的 XML 看起來像這樣。
換言之, 使返回的 XML 更像 JSON 一樣簡單且易於閱讀, 而不存在架構和命名空間詳細資訊, 只是資料和連結。如果結果比這更複雜, XML 的成本將會驚人。根據我的經驗, 在過去的幾年中, 沒有人使用過 XML 回應, 它的開銷太大了。
請注意, 如果您需要這種類型的東西, JSON 架構提供了架構樣式的驗證功能。
創建細細微性資源
開始時, 最好創建類比系統底層應用程式域或資料庫體系結構的 api。最終, 您需要使用多個底層資源的聚合服務來減少繁瑣。但是, 以後從單個資源創建更大的資源比從較大的聚合中創建細細微性或單個資源要容易得多。讓自己輕鬆一點, 從小而容易定義的資源開始, 在這些方面提供 CRUD 功能。以後可以創建那些面向用例的、繁瑣的資源。
考慮連通性
REST 的原則之一是連通性-通過超媒體連結 (搜索 HATEOAS)。儘管服務在沒有它們的情況下仍然有用, 但在回應中返回連結時, api 變得更加自我描述和可發現。至少, "自" 連結引用會通知用戶端資料是如何檢索的。此外, 利用 HTTP 位置標頭來包含通過 POST (或放入) 創建資源的連結。對於在支援分頁的回應中返回的集合, "第一個"、"尾頁"、"next" 和 "上一個" 連結的最小值都非常有用。
關於連結格式, 有很多。HTTP Web 連結規範 (RFC5988) 解釋如下連結:
連結是由國際化資源識別碼 (虹膜) [RFC3987] 標識的兩個資源之間的類型化連接, 它由以下內容組成:可以將連結視為表單的語句 "{上下文 iri} 在 {目標 IRI} 上有 {關聯類型} 資源, 它具有 {目標屬性}"。
- 一個上下文,
- 連結關聯類型
- 一個目標 IRI 和
- (可選) 目標屬性。
至少, 按照規範中的建議, 將連結放在 HTTP 連結標頭中, 或者在 json 表示中接受此 HTTP 連結樣式 (如 Atom 樣式連結) 的 json 表示形式 (請參見: RFC4287)。稍後, 您可以在更複雜的連結樣式 (如HAL + JSON、警笛、集合 + json和/或JSON-LD等) 中分層, 因為您的 REST api 變得更加成熟。
希望這邊文章發出來後,我想在開頭所提出的問題都能得到解決了。
也希望能讓更多人理解RESTful API設計的用意,以及如何設計出符合風格的RESTful API格式。
也許會有許多人說,這是為了___需求,所以這是不得已的情況,但Wiki中也說明了
「六個指導性約束定義了一個RESTful系統。[8] [10]這些約束限制了服務器處理和響應客戶請求的方式,以便通過在這些約束內操作,服務獲得期望的非功能屬性,例如性能,可伸縮性,簡單性,可修改性,可見性便攜性和可靠性。[2]如果某個服務違反了任何所需的約束,則不能將其視為RESTful。」
歡迎您的加入,讓這個社群更加美好!