前後端分離開發,RESTful 接口應該這樣設計

譯者 | 唐尤華 

dzone.com/refcardz/rest-foundations-restful

前言

REST(Representational State Transfer)架構風格是一種世界觀,把信息提升爲架構中的一等公民。通過 REST 可以實現系統的高性能、可伸縮、通用性、簡單性、可修改性和可擴展等特性。這篇文章解釋了主要的 HTTP 操作,對 HTTP 響應碼進行描述,並列舉相關開發庫和框架。此外,本文還提供了額外的資源,對每個主題進行了更深入的探討。

1. 簡介

REST 架構風格不是一種可以購買的技術,也不是一個可以添加到軟件開發項目中的開發庫。首先也是最重要的,REST 是一種世界觀,把將信息提升爲構建架構中的一等公民。

Roy Fielding 的博士論文“架構風格和基於網絡的軟件架構設計”介紹和整理了“RESTful”系統的思想和相關術語。這是一篇學術論文,雖然使用正式語言,但是仍然易於理解並且提供了實踐基礎。

總結一下,RESTful 通過體系結構的特定選擇能從部署的系統中獲得理想特性。儘管這種風格從正文選擇定義的約束細節並沒有爲所有場合設計,但是的確可以廣泛適用。

由於 Web 對消費者偏好有多重影響,REST 風格的倡導者鼓勵企業組織在其邊界內使用相同原則,就像他們在面向外部客戶的網頁上做的那樣。本文將討論現代 REST Web 實現中的基本約束和屬性。

1.1 基礎概念

REST 表示什麼含義?以無狀態方式傳輸、訪問和操作文本數據。當正確部署後,REST 爲互聯網上不同應用程序之間提供了一致的互操作性。無狀態(stateless)這個術語至關重要,它使得應用程序可以用不可知的方式進行通信。RESTful API 通過統一資源定位符地址(URL)公開服務。URL 名稱將資源的區分爲接受內容或返回內容。RFC 1738 中定義了 URL scheme,可以在這裏找到: https://tools.ietf.org/rfc/rfc1738.txt

RESTful URL 類似於下面這個 library API:http://fakelibrary.org/library

實際公開的不一定是某種任意的服務,而是代表對消費者有價值的信息資源。URL 作爲資源句柄,可以請求、更新或刪除內容。

開始把服務發佈到某個地方,然後開始與 REST 服務進行交互。返回的內容可能是 XML、JSON 格式,或者更確切地說是像 Atom 或自定義 MIME 類型等超媒體格式。雖然一般建議儘可能重用現有的格式,但是對正確設計的媒體類型正在變得越來越寬容。

需要請求資源的時候,客戶機會發一個超文本傳輸協議(HTTP)GET 請求,例如在瀏覽器中鍵入一個 URL 然後點擊回車,選擇書籤,或者點擊錨引用鏈接。

通過編程方式與 RESTful API 交互,有數十個客戶端 API 或工具可供選擇。使用 curl 命令行工具,可以輸入以下命令:

curl http://fakelibrary.org/library

上面的命令使用默認格式,但你可能不需要這種格式的信息。幸運的是 HTTP 有一種機制,可以指定返回信息的格式。在請求中指定 "Accept" 頭,如果服務器支持這種格式,會以指定的格式返回。這個過程稱爲內容協商,這是 HTTP 中未被充分利用的功能之一,可以使用一個類似於上面例子中的 curl 命令來指定:

curl –H "Accept:application/json" http://fakelibrary.org/library

由於資源名稱與內容格式是獨立的,從而讓請求不同格式信息成爲可能。雖然 REST 中的 “R” 的含義是 “表現”而非“資源”,但是應該在構建系統時允許客戶端指定請求的內容格式,請牢記這一點。在我們的例子中 library API 可能包含以下 URL:

1、http://fakelibrary.org/library圖書館基本信息,搜索圖書、DVD等相關資源基本功能的鏈接。

2、http://fakelibrary.org/book存放書籍的“信息空間”。從概念上說,這裏可能會存放所有的書籍。顯然,如果這個問題得到解決,我們不會希望返回所有圖書,而是希望通過類別、搜索關鍵詞等來檢索圖書。

3、http://fakelibrary.org/book/category/1234在書籍的信息空間裏,我們可以指定類別瀏覽,例如成人小說、兒童書籍、園藝書籍等。使用杜威十進制圖書分類法是可行的,但我們也可以想象自定義分組。問題的關鍵在於,這種“信息空間”可能是無限的,而且可能收到人們實際關心的信息類型影響。

4、http://fakelibrary.org/book/isbn/978-0596801687提到某本具體的書,應該包括書名、作者、出版商、系統中的拷貝數、可用拷貝數等信息。

譯註:杜威十進制圖書分類法由美國圖書館專家麥爾威·杜威發明,於1876年首次發表,歷經22次的大改版。該分類法以三位數字代表分類碼,共可分爲10個大分類、100箇中分類及1000個小分類。

就圖書館用戶而言,上面提到的這些 URL 可能就是隻讀的,但是圖書館員使用應用程序時實際上可以操作這些資源。

例如添加一本新書,可以向 main/book 地址 POST 一個 XML。使用 curl 提交,看起來可能像這樣:

curl –u username:password -d @book.xml -H "Content-type: text/xml" http://fakelibrary.org/book

此時,服務器可能會對提交的內容進行校驗,創建與圖書相關的記錄,並返回響應代碼201——表示已創建新資源。新資源的 URL 可以在響應的 Location 頭中找到。

RESTful 請求一個重要特性:每次請求都包含了充足的狀態信息來響應請求。這爲服務器的可見性和無狀態創造了條件,併爲擴展系統和識別發送的請求內容提供了理想特性。對於緩存結果也非常有幫助。服務器地址和請求狀態組合成可計算的 hash 鍵值,並形成一個結果集:

http://fakelibrary.org + /book/isbn/978-0596801687

接下來我們會先介紹 GET 請求。客戶端在需要時發出 GET 請求獲取指定資源。客戶端可以在本地緩存請求結果,服務器可以在遠程緩存結果,系統的中間層可以在請求鏈路中間緩存結果。這是一個與具體應用程序無關的特性,可以加入系統設計中。

正因爲可以操作資源,也就意味着並不是每個人都可以這樣做。我們完全可以建立一個防護模型,要求用戶在操作前驗證身份,證明他們具有該操作的授權。在本文的最後,將提供一些提升 RESTful 服務安全性的內容。

2. REST Vs SOAP

SOAP:簡單對象訪問協議(Simple Object Access Protocol)。是交換數據的一種協議規範,是一種輕量的、簡單的、基於XML的協議。一條 SOAP 消息就是一個普通的 XML 文檔,包含必需的 Envelope 元素、可選的 Header 元素、必需的 Body 元素和可選的 Fault 元素。

把 REST 與 SOAP 劃等號是錯誤的。在這兩者之間進行比較,帶來的困擾遠多於好處。簡單來說,它們不是一回事。儘管可以用這兩種方法解決許多架構問題,但是它們不能相互替換。

這種混淆很大程度上源於對 “REST 是通過 URL 調用 Web 服務”這句話的誤解。這種觀點與 RESTful 架構的功能相距甚遠。如果不全面深入理解 RESTful 的架構實現,就很容易誤解 REST 實踐的本意。

利用 REST 的最佳方式,是將生產和消費過程中的信息與技術分離實現解耦,進而更好地管理系統,讓架構具備以下特性:

1、高性能

2、可擴展

3、通用

4、簡潔

5、可修改

這並不是說,基於 SOAP 構建的系統不能具備上述特性。而是當技術、組織或過程的複雜性造成不能在單個事務中完成請求的生命週期時,這種情況 SOAP 能夠發揮最佳效果。

3. Richardson 成熟度模型

Leonard Richardson 引入了一種成熟度模型,部分闡述了 SOAP 與 REST 之間的區別,並提供一種對不同類型的系統進行分類的框架。許多人不恰當地稱之爲 “REST”。可以將這種分類看作系統中不同 Web 技術組件緊密程度的度量標準:包括信息資源、HTTP 作爲應用層協議和作超媒體作爲控制媒介。

稱其爲“成熟度模型”似乎意味着應該只構建“成熟度”最高的系統。這種看法是不合適的。第 2 級是有價值的,從 2 級向 3 級轉變通常只是採用了一種新的 MIME 類型。然而,從 0 級到 3 級的轉變要困難得多,因此增量式升級轉變通常也會增值。

首先,確定希望公開哪些信息資源。採用 HTTP 作爲處理這些信息資源的應用協議,包括內容協商。接下來,當一切就緒時,使用基於超媒體的 MIME 類型,這樣就可以充分享受 REST 的好處了。

4. 動詞

動詞是用來與服務器資源交互的方法或操作。RESTful 系統中有限的動詞讓剛接觸該的使用者感到困惑和沮喪。看似武斷和不必要的約束,目的是鼓勵以應用程序無關的形式提供可預測的行爲。通過明確、清晰地定義這些動詞的行爲,客戶端可以在網絡中斷或故障時自主處理。

精心設計的 RESTful 系統主要使用 4 個 HTTP 動詞。

4.1 GET

GET 請求是最常用的 Web 動詞。GET 請求將命名資源從服務器傳輸到客戶端。儘管客戶端不需要知道請求的資源內容,但是請求返回的結果是帶元數據標記的字節流,這表明客戶端應該知道如何解釋資源。在 Web 中通常用 “text/html” 或 “application/xhtml+xml” 表示。正如之前提到的那樣,只要服務器支持,客戶端可以通過內容協商提前指定請求的返回格式。

GET 請求關鍵點之一,不要修改服務器端的任何內容。這是一個基本的安全要求,也是不熟悉 REST 的開發者犯的最大錯誤之一。你可能會遇到這樣的 URL:

http://example.com/res/action=update?data=1234

不要這樣做! 由於 GET 請求安全性允許緩存請求,這會讓正在構建的 RESTful 系統陷入混亂。GET 請求也意味着冪等性,即多次請求不會對系統產生任何影響。這是基於分佈式基礎設施的一個重要特性。如果進行 GET 請求時被打斷,由於冪等性,客戶端可以再次發起請求。這點非常重要。在設計良好的基礎結構中,客戶端可以從任意應用程序發起請求。雖然一定會有與應用程序相關的特定行爲,但是加入與應用程序無關的行爲越多,系統就會越有彈性,也更容易維護。

4.2 POST

在辨別 POST 和 PUT 動詞意圖的時候,情況開始變得不那麼清晰。根據定義,二者似乎都可以被客戶端用來創建或更新服務器資源,然而它們的用途各有不同。

當無法預測請求創建的資源的標識時,客戶端會使用 POST 請求。在新增僱員、下訂單或提交表單的時候,我們無法預測服務器將如何命名正在創建的資源。這就是爲什麼將資源提交給類似 Servlet 這樣的程序處理。接下來,服務器會接受請求、校驗請求、驗證用戶憑據等。成功處理後,服務器將返回 201 HTTP 響應代碼,其中包含一個 “Location” 頭,代表新創建的資源的位置。

注意: 有些人將 POST 視爲創建資源的 GET 會話。他們會對創建的資源通過 body 返回200,而不是返回 201。這似乎是避免二次請求的一種快捷方式,但是這種做法混合了 POST 和 GET,讓緩存資源的潛在影響變得微妙。儘量避免因爲走捷徑而犧牲大局。短期看這似乎是值得的,但隨着時間的推移,這些捷徑疊加起來可能會帶來不利的影響。

POST 動詞的另一個主要用途是“追加(Append)”資源信息,即增量編輯或部分更新,而不是提交完整的資源。這裏應使用 PUT 操作。對已知資源使用 POST 更新,可用於向訂單添加新送貨地址或更新購物車中某個商品的數量。

由於是更新資源的部分信息,POST 既不安全也不冪等。

POST 的最後一種常見用法是提交查詢。將查詢的內容或表單內容進行 URL 編碼後提交給服務執行查詢。通常可以直接返回 POST 結果,因爲沒有與查詢相關的標識。

注意: 建議將這樣的查詢轉換爲信息資源本身。如果採用 POST 查詢,可以考慮採用 GET 請求,後者支持緩存。你可以與其他人分享這個鏈接。

4.3 PUT

由於 HTML 表單目前還不支持 PUT,許多開發人員基本上會忽略 PUT 動詞。然而,PUT 有一個重要作用並且是 RESTful 系統完整願景的一部分。

客戶端可以向指定 URL 發 PUT 請求,服務器用請求中的數據執行覆蓋操作。PUT 請求在某種程度上是等冪的,而 POST 更新不是。

如果客戶端在 PUT 覆蓋請求時被打斷,由於重新發送覆蓋操不會造成任何後果,因此可以再次發送。客戶端具備管理狀態能力,所以直接重發覆蓋命令即可。

注意: 這種協議層處理並不意味着要取消更高級別(如應用層)的事務,但是同樣地,它也是一種體系結構上理想的屬性,可以在應用層以下使用。

如果客戶端能夠提前瞭解資源的標識,那麼 PUT 也可用於創建資源。正如我們在 POST 部分中討論的那樣,通常不會出現這種情況。但是如果客戶端能夠控制服務器端信息空間,那麼這種操作也是合理的。

4.4 DELETE

在公共網絡上 DELETE 動詞沒有被廣泛使用(謝天謝地!)。然而,對於控制信息空間非常有用,它是資源生命週期中非常有用的一部分。

DELETE 請求意在實現等冪。可能由於網絡故障 DELETE 請求被打斷,這時我們希望客戶端繼續嘗試。第一次請求無論成功與否,資源都應該返回204(無指定內容)。對之前已刪除的資源或不存在的資源可能需要一些額外處理,兩種情況都應該返回404。一些安全策略要求爲不存在的和已刪除的資源返回404,這樣 DELETE 請求就不會泄漏有關資源是否存在的信息。

還有另外三個沒有廣泛使用但是有價值的動詞。

4.5 HEAD

HEAD 動詞用來請求資源,但不實際檢索。客戶端可以通過 HEAD 檢查資源是否存在,並檢查資源相關的元數據。

4.6 OPTIONS

OPTIONS 動詞也可以用來查詢服務器相關資源的情況,方法是詢問哪些其它動詞可用於該資源。

4.7 PATCH

最新的動詞 PATCH 直到 2010 年才正式採納爲 HTTP 的一部分。旨在提供一種標準化方式來表示部分更新。PATCH 請求通過標準格式讓交互的意圖更明確。這是推薦使用 PATCH 而非 POST 的原因,儘管 POST 可以用於任何事情。IETF 發佈了 RFC 文檔,定義用於 PATCH 操作的 XML 和 JSON。

如果客戶端 PATCH 請求的 header 中帶 If-Match,則此部分爲冪等更新。可以重試中斷的請求,因爲如果第一次請求成功,那麼 If-Match header 會不同於新狀態。如果相同,則未處理原始請求可應用 PATCH。

5. 響應碼

HTTP 響應碼爲我們在客戶端和服務器之間的對話提供了豐富的請求狀態信息。大多數人只熟悉一般意義上的200、403、404或者500,但是還有更多有用的代碼可供使用。這裏表格並不全面,但是它們涵蓋了許多在 RESTful 環境中應該考慮使用的最重要代碼。數字可按照以下類別分組:

1、1XX:信息類

2、2XX:操作成功

3、3XX:重定向

4、4XX:客戶端錯誤

5、5XX:服務器錯誤

第一組響應碼錶明客戶端的請求格式正確且處理成功。具體操作如下表所示:

表1 成功的客戶端請求

表2 — 客戶端重定向請求

表 3 中的響應代碼表示客戶端請求無效,如果條件不發生變化,重新請求仍無法處理。這些故障可能有請求格式錯誤、未授權的請求、請求的資源不存在等。

表3 客戶端請求錯誤

最後,表4中的響應代碼表示服務器暫時無法處理客戶端請求(可能仍然無效)。客戶端應當在將來的某個時候重新請求。

表4 服務器處理請求錯誤

服務根據其自身功能要求具有不同程度的可擴展性。

注意:試試響應代碼 418,它會返回簡潔有力的回覆:"我是一個茶壺。"

5.1 REST 資源

5.1.1 論文

Fielding 博士的論文《架構的風格與基於網絡的軟件架構設計》是對 RESTful 思想的主要介紹:http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm

5.1.2 RFC 規範

REST 常見用法的技術規範由**國際互聯網工程任務組(IETF)定義,按照請求評議(RFC)**流程完善。規範由數字定義,並隨着時間推移不時更新版本,以替換已經過時的文件。目前,這裏有最新的相關 RFC 文件。

5.1.2.1 URI

RFC 3986 定義了 URI 命名方案的通用語法。URI 是一種命名方案,包含了對其他如網址、支持名字子空間等編碼方案。網址:http://www.ietf.org/rfc/rfc3986.txt

5.1.2.2 URL

Url 是 URI 的一種形式,其中嵌入了充足的信息(通常是訪問方案和地址),用於解析和定位資源統一資源定位符。網址:http://www.ietf.org/rfc/rfc1738.txt

5.1.2.3 IRI

國際化資源標識符(IRI)在概念上是一個用 Unicode 編碼的 URI,用於在 Web 上使用的標識符中支持世界上各種語言的字符。IETF 選擇創建一個新的標準,而不是改變 URI 方案本身,以避免破壞現有的系統並明確區分這兩種方法。那些支持 IRI 的人故意這樣做。還定義了在 IRI 和 URI 之間進行轉換的映射方案。網址:http://www.ietf.org/rfc/rfc3987.txt

5.1.2.4 HTTP

HTTP 1.1 版本定義了一個應用程序協議,用於操作通常以超媒體格式表示的信息資源。雖然它是一個應用級協議,但通常不與應用程序綁定,由此產生了重要的體系結構優勢。大多數人認爲 HTTP 和超文本標記語言文(HTML)就是“Web”,但是 HTTP 在非面向文檔的系統開發中也很有用。網址:http://www.ietf.org/rfc/rfc2616.txt

5.1.2.5 PATCH 格式

JavaScript 對象表示法(JSON)Patch 網址:https://www.ietf.org/rfc/rfc6902.txt

XML Patch 網址:https://www.ietf.org/rfc/rfc7351.txt

5.2 描述語言

人們對使用各種語言來描述 API 非常感興趣,通過描述語言可以更容易地編寫客戶端和服務器文檔,甚至生成骨架代碼。一些比較流行、有趣的描述語言包括:

5.2.1 RAML

RAML 是一種 YAML/JSON 語言,可以定義 2 級成熟度的 API。它支持可重用模式和特性,通過模式和特性實現功能 API 設計的標準化。網址:http://raml.org

5.2.2 Swagger

Swagger 是另一種 YAML/JSON 語言,支持定義2級成熟度的 API。它包含代碼生成器、編輯器、 API 文檔可視化功能,能夠與其他服務集成的。網址:http://swagger.io

5.2.3 Apiary.io

Apiary.io 是一個協作式的託管站點。它支持 Markdown 格式的 API 文檔,可以圍繞設計過程進行社交,並且支持模擬數據的託管實現,以便於在 API 實現之前對其進行測試。網址:http://apiary.io

5.2.4 Hydra-Cg

Hydra-Cg 是一種超媒體描述語言,通過像 JSON-LD 這樣的標準方便地實現數據關聯和並其它數據源的交互。網址:http://www.hydra-cg.com

5.3 實現

有一些用於構建、生成和使用 RESTful 系統的庫和框架。雖然任何 Web 服務器都可以配置成提供 REST API,但有了這些框架、庫和環境可以讓過程變得更容易。

以下概述了一些主流的環境:

5.3.1 JAX-RS

JAX-RS 規範爲 JEE 環境增加了對 REST 的支持。網址:https://jax-rs-spec.java.net

5.3.2 Restlet

Restlet API 是構建用於生產和消費 RESTful 系統的 Java API 先行者之一。它專注於爲客戶端和服務器生成一些非常乾淨、強大的 API。

Restlet Studio 是一個免費工具,能夠在 RAML 和基於 swagger 的 API 描述之間進行轉換,支持 Restlet、 Node 和 JAX-RS 服務器和客戶端的骨架和 Stub 代碼。網址:http://restlet.org

5.3.3 NetKernel

Netkernel 是一個比較有趣的 RESTful 系統。它基於微內核,是支持各種架構風格環境的代表。Netkernel 受益於在軟件體系結構中採用 Web 的經濟屬性。你可以把它想象成“在內部引入 REST”。雖然任何基於 REST 的系統在外面看起來都一樣,但在運行環境內部 NetKernel 看起來也一樣。網址:http://netkernel.org

5.3.4 Play

兩個主要的 Scala REST 框架之一。網址:https://www.playframework.com

5.3.5 Spray

兩個主要的 Scala REST 框架之一。它設計成配合 Akka actor 模型一起工作。網址:http://spray.io

5.3.6 Express

兩個主要的 Node.js REST 框架之一。網址:http://expressjs.com

5.3.7 hapi

兩個主要的 Node.js REST 框架之一。網址:http://hapijs.com

5.3.8 Sinatra

Sinatra 是一個領域特定語言(DSL),用來在 Ruby 中創建 RESTful 應用程序,網址:http://www.sinatrarb.com

5.4 客戶端

通過瀏覽器調用 REST API 是可行的,但是還有其它客戶端可用於測試和構建面向資源的系統。

5.4.1 curl

curl 是流行的庫和命令行工具之一,支持在各種資源上調用各種協議。網址:https://curl.haxx.se

5.4.2 httpie

httpie 是一個非常靈活和易用的客戶端,支持通過 HTTP 與資源進行交互。網址:https://httpie.org

5.4.3 Postman

健全的 API 測試需要能夠捕獲和重播請求,支持各種身份驗證和授權方案等功能。以前的命令行工具允許這樣做,但 Postman 是一個較新的桌面應用程序,讓這些工作對於開發團隊來說變得更容易。網址:https://www.getpostman.com

 

END

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