深刻淺出 REST(轉)

文章講的不錯,更具體一些,對實踐的指導意義更強數據庫

原文:https://www.infoq.cn/article/rest-introduction/瀏覽器


不知你是否意識到,圍繞着什麼纔是實現異構的應用到應用通訊的「正確」方式,一場爭論正進行的如火如荼:雖然當前主流的方式明顯地集中在基於 SOAP、WSDL 和 WS-* 規範的 Web Services 領域,但也有少數人用細小但洪亮的聲音主張說更好的方式是 REST,表述性狀態轉移(REpresentational State Transfer)的簡稱。在本文中,我不會涉及爭論的話題,而是嘗試對 REST 和 RESTful HTTP 應用集成作實用性的介紹。以個人經驗,有些話題一旦觸及就會引來衆多的討論,當涉及到這方面話題的時候,我會深刻詳細地闡述。緩存

REST 關鍵原則

大部分對 REST 的介紹是以其正式的定義和背景做爲開場的。但這兒且先按下不表,我先提出一個簡單扼要的定義:REST 定義了應該如何正確地使用(這和大多數人的實際使用方式有很大不一樣)Web 標準,例如 HTTP 和 URI。若是你在設計應用程序時能堅持 REST 原則,那就預示着你將會獲得一個使用了優質 Web 架構(這將讓你受益)的系統。總之,五條關鍵原則列舉以下:安全

  • 爲全部「事物」定義 ID
  • 將全部事物連接在一塊兒
  • 使用標準方法
  • 資源多重表述
  • 無狀態通訊

下面讓咱們進一步審視這些原則。服務器

爲全部「事物」定義 ID

在這裏我使用了「事物」來代替更正式準確的術語「資源」,由於一條如此簡單的原則,不該該被淹沒在術語當中。思考一下人們構建的系統,一般會找到一系列值得被標識的關鍵抽象。每一個事物都應該是可標識的,都應該擁有一個明顯的 ID——在 Web 中,表明 ID 的統一律念是:URI。URI 構成了一個全局命名空間,使用 URI 標識你的關鍵資源意味着它們得到了一個惟1、全局的 ID。網絡

對事物使用一致的命名規則(naming scheme)最主要的好處就是你不須要提出本身的規則——而是依靠某個已被定義,在全球範圍中幾乎完美運行,而且能被絕大多數人所理解的規則。想一下你構建的上一個應用(假設它不是採用 RESTful 方式構建的)中的任意一個高級對象(high-level object),那就頗有可能看到許多從使用惟一標識中受益的用例。好比,若是你的應用中包含一個對顧客的抽象,那麼我能夠至關確定,用戶會但願將一個指向某個顧客的連接,能經過電子郵件發送到同事那裏,或者加入到瀏覽器的書籤中,甚至寫到紙上。更透徹地講:若是在一個相似於 Amazon.com 的在線商城中,沒有用惟一的 ID(一個 URI)標識它的每一件商品,可想而知這將是多麼可怕的業務決策。session

當面對這個原則時,許多人驚訝於這是否意味着須要直接向外界暴露數據庫記錄(或者數據庫記錄 ID)——自從多年以來面向對象的實踐告誡咱們,要將持久化的信息做爲實現細節隱藏起來以後,哪怕是剛有點想法都常會令人驚恐。可是這條原則與隱藏實現細節二者之間並無任何衝突:一般,值得被 URI 標識的事物——資源——要比數據庫記錄抽象的多。例如,一個定單資源能夠由定單項、地址以及許多其它方面(可能不但願做爲單獨標識的資源暴露出來)組成。標識全部值得標識的事物,領會這個觀念能夠進一步引導你創造出在傳統的應用程序設計中不常見的資源:一個流程或者流程步驟、一次銷售、一次談判、一份報價請求——這都是應該被標識的事物的示例。一樣,這也會致使建立比非 RESTful 設計更多的持久化實體。架構

下面是一些你可能想到的 URI 的例子:app

http://example.com/customers/1234
http://example.com/orders/2007/10/776654
http://example.com/products/4554
http://example.com/processes/salary-increase-234 

正如我選擇了建立便於閱讀的 URI——這是個有用的觀點,儘管不是 RESTful 設計所必須的——應該能十分容易地推測出 URI 的含義:它們明顯地標識着單一「數據項」。可是再往下看:less

http://example.com/orders/2007/11
http://example.com/products?color=green 

首先,這兩個 URI 看起來與以前的稍有不一樣——畢竟,它們不是對一件事物的標識,而是對一類事物集合的標識(假定第一個 URI 標識了全部在 2007 年 11 月份提交的定單,第二個則是綠顏色產品的集合)。可是這些集合自身也是事物(資源),也應該被標識。

注意,使用惟1、全局統一的命名規則的好處,既適用於瀏覽器中的 Web 應用,也適用於機對機(machine-to-machine,m2m)通訊。

來對第一個原則作下總結:使用 URI 標識全部值得標識的事物,特別是應用中提供的全部「高級」資源,不管這些資源表明單一數據項、數據項集合、虛擬亦或實際的對象仍是計算結果等。

將全部事物連接在一塊兒

接下來要討論的原則有一個有點使人懼怕的正式描述:「超媒體被看成應用狀態引擎(Hypermedia as the engine of application state)」,有時簡寫爲 HATEOAS。(嚴格地說,這不是我說的。)這個描述的核心是超媒體概念,換句話說:是連接的思想。連接是咱們在 HTML 中常見的概念,可是它的用處毫不侷限於此(用於人們網絡瀏覽)。考慮一下下面這個虛構的 XML 片斷:

<order self="http://example.com/customers/1234"> 
   <amount>23</amount> 
   <product ref="http://example.com/products/4554"> 
   <customer ref="http://example.com/customers/1234"> 
</customer> </product></order>

若是你觀察文檔中 product 和 customer 的連接,就能夠很容易地想象到,應用程序(已經檢索過文檔)如何「跟隨」連接檢索更多的信息。固然,若是使用一個遵照專用命名規範的簡單「id」屬性做爲連接,也是可行的——可是僅限於應用環境以內。使用 URI 表示連接的優雅之處在於,連接能夠指向由不一樣應用、不一樣服務器甚至位於另外一個大陸上的不一樣公司提供的資源——由於 URI 命名規範是全球標準,構成 Web 的全部資源均可以互聯互通。

超媒體原則還有一個更重要的方面——應用「狀態」。簡而言之,實際上服務器端(若是你願意,也能夠叫服務提供者)爲客戶端(服務消費者)提供一組連接,使客戶端能經過連接將應用從一個狀態改變爲另外一個狀態。稍後咱們會在另外一篇文章中探究這個方面的影響;目前,只須要記住:連接是構成動態應用的很是有效的方式。

對此原則總結以下:任何可能的狀況下,使用連接指引能夠被標識的事物(資源)。也正是超連接造就瞭如今的 Web。

使用標準方法

在前兩個原則的討論中暗含着一個假設:接收 URI 的應用程序能夠經過 URI 明確地作一些有意義的事情。若是你在公共汽車上看到一個 URI,你能夠將它輸入瀏覽器的地址欄中並回車——可是你的瀏覽器如何知道須要對這個 URI 作些什麼呢?

它知道如何去處理 URI 的緣由在於全部的資源都支持一樣的接口,一套一樣的方法(只要你樂意,也能夠稱爲操做)集合。在 HTTP 中這被叫作動詞(verb),除了兩個你們熟知的(GET 和 POST)以外,標準方法集合中還包含 PUT、DELETE、HEAD 和 OPTIONS。這些方法的含義連同行爲許諾都一塊兒定義在 HTTP 規範之中。若是你是一名 OO 開發人員,就能夠想象到 RESTful HTTP 方案中的全部資源都繼承自相似於這樣的一個類(採用類 Java、C#的僞語法描述,請注意關鍵的方法):

class Resource {
     Resource(URI u);
     Response get();
     Response post(Request r);
     Response put(Request r);
     Response delete();
} 

因爲全部資源使用了一樣的接口,你能夠依此使用 GET 方法檢索一個表述(representation)——也就是對資源的描述。由於規範中定義了 GET 的語義,因此能夠確定當你調用它的時候不須要對後果負責——這就是爲何能夠「安全」地調用此方法。GET 方法支持很是高效、成熟的緩存,因此在不少狀況下,你甚至不須要向服務器發送請求。還能夠確定的是,GET 方法具備冪等性[譯註:指多個相同請求返回相同的結果]——若是你發送了一個 GET 請求沒有獲得結果,你可能不知道緣由是請求未能到達目的地,仍是響應在反饋的途中丟失了。冪等性保證了你能夠簡單地再發送一次請求解決問題。冪等性一樣適用於 PUT(基本的含義是「更新資源數據,若是資源不存在的話,則根據此 URI 建立一個新的資源」)和 DELETE(你徹底能夠一遍又一遍地操做它,直到得出結論——刪除不存在的東西沒有任何問題)方法。POST 方法,一般表示「建立一個新資源」,也能被用於調用任過程,於是它既不安全也不具備冪等性。

若是你採用 RESTful 的方式暴露應用功能(若是你樂意,也能夠稱爲服務功能),那這條原則和它的約束一樣也適用於你。若是你已經習慣於另外的設計方式,則很難去接受這條原則——畢竟,你極可能認爲你的應用包含了超出這些操做表達範圍的邏輯。請容許我花費一些時間來讓你相信不存在這樣的狀況。

來看下面這個簡單的採購方案例子:

能夠看到,例子中定義了兩個服務程序(沒有包含任何實現細節)。這些服務程序的接口都是爲了完成任務(正是咱們討論的 OrderManagement 和 CustomerManagement 服務)而定製的。若是客戶端程序試圖使用這些服務,那它必須針對這些特定接口進行編碼——不可能在這些接口定義以前,使用客戶程序去有目的地和接口協做。這些接口定義了服務程序的應用協議(application protocol)。

在 RESTful HTTP 方式中,你將經過組成HTTP 應用協議的通用接口訪問服務程序。你可能會想出像這樣的方式:

能夠看到,服務程序中的特定操做被映射成爲標準的 HTTP 方法——爲了消除歧義,我建立了一組全新的資源。「這是騙人的把戲」,我聽見你叫嚷着。不,這不是欺騙。標識一個顧客的 URI 上的 GET 方法正好至關於 getCustomerDetails 操做。有人用三角形形象化地說明了這一點:

把三個頂點想象爲你能夠調節的按鈕。能夠看到在第一種方法中,你擁有許多操做,許多種類的數據以及固定數量的「實例」(本質上和你擁有的服務程序數量一致)。在第二種方法中,你擁有固定數量的操做,許多種類的數據和許多調用固定方法的對象。它的意義在於,證實了經過這兩種方式,你基本上能夠表示任何你喜歡的事情。

爲何使用標準方法如此重要?從根本上說,它使你的應用成爲 Web 的一部分——應用程序爲 Web 變成 Internet 上最成功的應用所作的貢獻,與它添加到 Web 中的資源數量成比例。採用 RESTful 方式,一個應用可能會向 Web 中添加數以百萬計的客戶 URI;若是採用 CORBA 技術並維持應用的原有設計方式,那它的貢獻大抵只是一個「端點(endpoint)」——就比如一個很是小的門,僅僅容許有鑰匙的人進入其中的資源域。

統一接口也使得全部理解 HTTP 應用協議的組件能與你的應用交互。通用客戶程序(generic client)就是從中受益的組件的例子,例如 curl、wget、代理、緩存、HTTP 服務器、網關還有 Google、Yahoo!、MSN 等等。

總結以下:爲使客戶端程序能與你的資源相互協做,資源應該正確地實現默認的應用協議(HTTP),也就是使用標準的 GET、PUT、POST 和 DELETE 方法。

資源多重表述

到目前爲止咱們一直忽略了一個稍微複雜的問題:客戶程序如何知道該怎樣處理檢索到的數據,好比做爲 GET 或者 POST 請求的結果?緣由是,HTTP 採起的方式是容許數據處理和操做調用之間關係分離的。換句話說,若是客戶程序知道如何處理一種特定的數據格式,那就能夠與全部提供這種表述格式的資源交互。讓咱們再用一個例子來闡明這個觀點。利用 HTTP 內容協商(content negotiation),客戶程序能夠請求一種特定格式的表述:

GET /customers/1234 HTTP/1.1
Host: example.com 
Accept: application/vnd.mycompany.customer+xml  

請求的結果多是一些由公司專有的 XML 格式表述的客戶信息。假設客戶程序發送另一個不一樣的請求,就以下面這樣:

GET /customers/1234 HTTP/1.1
Host: example.com 
Accept: text/x-vcard 

結果則多是 VCard 格式的客戶地址。(在這裏我沒有展現響應的內容,在其 HTTP Content-type 頭中應該包含着關於數據類型的元數據。)這說明爲何理想的狀況下,資源表述應該採用標準格式——若是客戶程序對 HTTP 應用協議和一組數據格式都有所「瞭解」,那麼它就能夠用一種有意義的方式與世界上任意一個 RESTful HTTP 應用交互。不幸的是,咱們不可能拿到全部東西的標準格式,可是,或許咱們能夠想到在公司或者一些合做夥伴中使用標準格式來營造一個小環境。固然以上狀況不只適用於從服務器端到客戶端的數據,反之既然——假若從客戶端傳來的數據符合應用協議,那麼服務器端就可使用特定的格式處理數據,而不去關心客戶端的類型。

在實踐中,資源多重表述還有着其它重要的好處:若是你爲你的資源提供 HTML 和 XML 兩種表述方式,那這些資源不只能夠被你的應用所用,還能夠被任意標準 Web 瀏覽器所用——也就是說,你的應用信息能夠被全部會使用 Web 的人獲取到。

資源多重表述還有另一種使用方式:你能夠將應用的 Web UI 歸入到 Web API 中——畢竟,API 的設計一般是由 UI 能夠提供的功能驅動的,而 UI 也是經過 API 執行動做的。將這兩個任務合二爲一帶來了使人驚訝的好處,這使得使用者和調用程序都能獲得更好的 Web 接口。

總結:針對不一樣的需求提供資源多重表述。

無狀態通訊

無狀態通訊是我要講到的最後一個原則。首先,須要着重強調的是,雖然 REST 包含無狀態性(statelessness)的觀念,但這並非說暴露功能的應用不能有狀態——

事實上,在大部分狀況下這會致使整個作法沒有任何用處。REST 要求狀態要麼被放入資源狀態中,要麼保存在客戶端上。或者換句話說,服務器端不能保持除了單次請求以外的,任何與其通訊的客戶端的通訊狀態。這樣作的最直接的理由就是可伸縮性—— 若是服務器須要保持客戶端狀態,那麼大量的客戶端交互會嚴重影響服務器的內存可用空間(footprint)。(注意,要作到無狀態通訊每每須要須要一些從新設計——不能簡單地將一些 session 狀態綁縛在 URI 上,而後就宣稱這個應用是 RESTful。)

但除此之外,其它方面可能顯得更爲重要:無狀態約束使服務器的變化對客戶端是不可見的,由於在兩次連續的請求中,客戶端並不依賴於同一臺服務器。一個客戶端從某臺服務器上收到一份包含連接的文檔,當它要作一些處理時,這臺服務器宕掉了,多是硬盤壞掉而被拿去修理,多是軟件須要升級重啓——若是這個客戶端訪問了從這臺服務器接收的連接,它不會察覺到後臺的服務器已經改變了。

理論上的 REST

我認可:以上我所說的 REST 不是真正的 REST,並且我可能有點過多地熱衷於簡單化。但由於我想有一個不同凡響的開場,因此沒有在一開始就介紹其正式的定義和背景。如今就讓咱們稍微簡要地介紹一下這方面的內容。

首先,先前我並無明確地區分 HTTP、RESTful HTTP 和 REST。要理解這些不一樣方面之間的關係,咱們要先來看看 REST 的歷史。

Roy T. Fielding 在他的博士學位論文(實際上你應該訪問這個連接——至少對於一篇學術論文來講,它是至關易讀的。此論文已被翻譯成中文)中定義了術語REST。Roy 曾是許多基本Web 協議的主要設計者,其中包括HTTP 和URIs,而且他在論文中對這些協議提出了不少想法。(這篇論文被譽爲「REST 聖經」,這是恰當的——畢竟,是做者發明了這個術語,因此在定義上,他寫的任何內容都被認爲是權威的。)在論文中,Roy 首先定義一種方法論來談論架構風格——高級、抽象的模式,來表達架構方法背後的核心理念。每個架構風格由一系列的約束(constraints)定義造成。架構風格的例子包括「沒有風格」(根本沒有任何約束)、管道和過濾器(pipe and filter)、客戶端/ 服務器、分佈式對象以及——你猜到它了——REST。

若是對你來講這些聽起來都太抽象了,那就對了——REST 在本質上是一個能夠被許多不一樣技術實現的高層次的風格,並且能夠被實例化——經過爲它的抽象特性賦上不一樣的值。好比,REST 中包含資源和統一接口的概念——也就是說,全部資源都應該對這些相同的方法做出反應。可是 REST 並無說明是哪些方法,或者有多少方法。

REST 風格的一個「化身」即是 HTTP(以及一套相關的一套標準,好比 URI),或者稍微抽象一些:Web 架構自身。接着上面的例子,HTTP 使用 HTTP 動詞做爲 REST 統一接口的「實例」。因爲 Fielding 是在 Web 已經(或者至少是大部分)「完善」了以後才定義的 REST 風格,有人可能會爭論二者是否是 100% 的匹配。可是不管如何,總體上來講 Web、HTTP 和 URI 僅僅是 REST 風格的一個主要實現。不過,因爲 Roy Fielding 便是 REST 論文的做者,又對 Web 架構設計有過深遠的影響,二者類似也在情理之中。

最後,我在前面一次又一次地使用着術語「RESTful HTTP」,緣由很簡單:許多使用 HTTP 的應用由於一些理由並無遵循 REST 原則,有人會說使用 HTTP 而不遵循 REST 原則就等同於濫用 HTTP。固然這聽起來有點狂熱——事實上違反 REST 約束的緣由一般是,僅僅由於每一個約束帶來的設計權衡可能不適合於一些特殊狀況。但一般,違背 REST 約束的緣由可歸咎於對其好處認知的缺少。來看一個明顯的反面案例:使用 HTTP GET 調用相似於刪除對象的操做,這違反了 REST 的安全約束和通常性常識(客戶程序不該爲此負責,服務器端開發人員大概不是有意而爲之)。但在隨後的文章中,我會說起更多這樣或那樣的對 HTTP 的濫用。

總結

本文試圖對 REST(Web 架構)背後的概念提供快速的介紹。RESTful HTTP 暴露功能的方式與 RPC、分佈式對象以及 Web Services 是不相同的;要真正理解這些不一樣是須要一些心態的轉變。無論你構建的應用是僅僅想暴露 Web UI 仍是想把 API 變成 Web 的一份子,瞭解下 REST 的原則仍是有好處的。

Stefan Tilkov是 InfoQ SOA 社區的首席編輯,而且是位於德國和瑞士的 innoQ公司的共同創始人、首席顧問和 REST 狂熱分子首領。

查看英文原文: A Brief Introduction to REST


譯者簡介:苑永凱,軟件設計師,畢業於山東大學。主要關注領域爲 Java EE 企業應用、Java EE 中間件技術以及敏捷開發方法實踐,微有心得。他的 Blog 爲 http://blog.csdn.net/ai92 ,您也能夠經過yuanyk[AT]gmail.com 與他聯繫。參與 InfoQ 中文站內容建設,請郵件至 china-editorial[at]infoq.com 

相關文章
相關標籤/搜索