InfoQ:深入淺出REST

來源:互聯網
上載者:User

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


他是由Roy Thomas Fielding博士在他的論文 《Architectural Styles and the Design of Network-based Software Architectures》中提出的一個術語。

REST本身只是為分布式超媒體系統設計的一種架構風格,而不是標準。

(

xiazdong 註:

1.按照以往的設計模式,很多URL後面都是一堆參數,對於使用者來說也是很不方便的,而REST主要關注的是URL的設定,把使用者的需求抽象成資源;

2.REST的無狀態:工作階段狀態全部都由用戶端儲存,伺服器端儲存資源狀態;

3.REST:Representational State Transfer;

4.REST不是標準,而是一種個人喜好的設計模式,將每個功能都當做一個資源;

5.如果用戶端發出請求資源,返回的是資源的一種表述形式(多重表述性);

6.HTTP僅僅是實現REST統一介面的一種形式;

)


REST關鍵原則


大部分對REST的介紹是以其正式的定義和背景作為開場的。但這兒且先按下不表,我先提出一個簡單扼要的定義:REST定義了應該如何正確地使用(這和大多數人的實際使用方式有很大不同)Web標準,例如HTTP和URI。如果你在設計應用程式時能堅持REST原則,那就預示著你將會得到一個使用了優質Web架構(這將讓你受益)的系統。總之,五條關鍵原則列舉如下: 為所有“事物”定義ID 將所有事物連結在一起 統一介面(xiazdong 註:每個資源限定只有HTTP的8個標準方法) 資源多重表述(HTML/XML/JSON) 無狀態通訊:用戶端和伺服器端不必儲存對方的詳細資料,伺服器只需要處理當前 request,而不必瞭解所有的request曆史;由於不能將狀態資料儲存在伺服器上的共用上下文中,因此增加了在一系列request中發送重複資料的開銷,嚴重的降低了效率。

下面讓我們進一步審視這些原則。


為所有“事物”定義ID


在這裡我使用了“事物”來代替更正式準確的術語“資源”,因為一條如此簡單的原則,不應該被淹沒在術語當中。思考一下人們構建的系統,通常會找到一系列值得被標識的關鍵抽象。每個事物都應該是可標識的,都應該擁有一個明顯的ID——在Web中,代表ID的統一概念是:URI。URI構成了一個全域命名空間,使用URI標識你的關鍵資源意味著它們獲得了一個唯一、全域的ID。

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

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

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

http://example.com/customers/1234     //id=1234的customerhttp://example.com/orders/2007/10/776654   //在2007年10月的id=776654的訂單http://example.com/products/4554    //id=4554的商品http://example.com/processes/salary-increase-234   //漲工資

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

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

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

注意,使用唯一、全域統一的命名規則的好處,既適用於瀏覽器中的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的所有資源都可以互聯互連。

超媒體原則還有一個更重要的方面——應用“狀態”。簡而言之,實際上伺服器端(如果你願意,也可以叫服務提供者)為用戶端(服務消費者)提供一組連結,使用戶端能通過連結將應用從一個狀態改變為另一個狀態。稍後我們會在另一篇文章中探究這個方面的影響;目前,只需要記住:連結是構成Live App的非常有效方式。

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


使用標準方法


在前兩個原則的討論中暗含著一個假設:接收URI的應用程式可以通過URI明確地做一些有意義的事情。如果你在公用汽車上看到一個URI,你可以將它輸入瀏覽器的地址欄中並斷行符號——但是你的瀏覽器如何知道需要對這個URI做些什麼呢。

瀏覽器知道如何去處理URI的原因在於所有的資源都支援同樣的介面(GET/POST/....),一套同樣的方法(只要你樂意,也可以稱為操作)集合。在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應用協議的通用介面訪問服務程式。你可能會想出像這樣的方式:

可以看到,服務程式中的特定操作(list customers/....)被映射成為標準的HTTP方法(GET/POST)(註:通過annotation映射)——為了消除歧義,我建立了一組全新的資源。“這是騙人的把戲”,我聽見你叫嚷著。不,這不是欺騙。標識一個顧客的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.1Host: example.com Accept: application/vnd.mycompany.customer+xml  

請求的結果可能是一些由公司專有的XML格式表述的客戶資訊。假設客戶程式發送另外一個不同的請求,就如下面這樣:

GET /customers/1234 HTTP/1.1Host: 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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.