天天看點

關于REST的一點想法

2007-07-19 15:5519382人閱讀評論(8)收藏舉報

resturl網絡應用mvcrailssearch

這陣子正打算用Rails做個東東,是以開始系統地學習起了Rails。巧合的是,大概兩周前,dlee邀請我加入Fielding博士關于REST的那篇論文的翻譯團隊。可以說Rails和REST這兩個最熱門的詞彙幾乎同時擠入了我的生活。随着我對Rails的學習和對[Fielding]的翻譯,我也開始對REST産生了一些不太成熟的想法,寫在這裡與大家分享,同時也起到抛磚引玉的作用,歡迎大家讨論。

先複習一下REST的基本思想。[Fielding]把REST形式化地定義為一種架構風格(architecture style),它有架構元素(element)和架構限制(constraint)組成。這些概念比較晦澀難懂,而且我們做工程的往往并不需要形而上的了解。我們隻知道,REST是一種針對網絡應用的設計和開發方式,可以降低開發的複雜性,提高系統的可伸縮性。REST提出了一些設計概念和準則:

網絡上的所有事物都被抽象為資源(resource);

每個資源對應一個唯一的資源辨別(resource identifier);

通過通用的連接配接器接口(generic connector interface)對資源進行操作;

對資源的各種操作不會改變資源辨別;

所有的操作都是無狀态的(stateless)。

對于當今最常見的網絡應用來說,resource identifier是url,generic connector interface是HTTP,第4條準則就是我們常說的url不變性。這些概念中的resouce最容易使人産生誤解。resouce所指的并不是資料,而是資料+特定的表現形式(representation),這也是為什麼REST的全名是Representational State Transfer的原因。舉個例子來說,“本月賣得最好的10本書”和“你最喜歡的10本書”在資料上可能有重疊(有一本書即賣得好,你又喜歡),甚至完全相同。但是它們的representation不同,是以是不同的resource。

REST之是以能夠簡化開發,是因為其引入的架構限制,比如Rails 1.2中對REST的實作預設把controller中的方法限制在7個:index、show、new、edit、create、update和destory,這實際上就是對CURD的實作。更進一步講,Rails(也是當今大部分網絡應用)使用HTTP作為generic connector interface,HTTP則把對一個url的操作限制在了4個之内:GET、POST、PUT和DELETE。

REST之是以能夠提高系統的可伸縮性,是因為它強制所有操作都是stateless的,這樣就沒有context的限制,如果要做分布式、做叢集,就不需要考慮context的問題了。同時,它令系統可以有效地使用pool。REST對性能的另一個提升來自其對client和server任務的配置設定:server隻負責提供resource以及操作resource的服務,而client要根據resource中的data和representation自己做render。這就減少了伺服器的開銷。

既然REST有這樣的好處,那我們應該義無反顧地擁抱它啊!目前一些大牛(像DHH)都已經開始投入到了REST的世界,那我們這些人應該做什麼或者說思考寫什麼你呢?我覺得我們應該思考兩個問題:

如何使用REST;

REST和MVC的關系。

第一個問題假設REST是我們應該采用的架構,然後讨論如何使用;第二個問題則要說明REST和目前最普遍應用的MVC是什麼關系,互補還是取代?

我們先來談談第一個問題,如何使用REST。我感覺,REST除了給我們帶來了一個嶄新的架構以外,還有一個重要的貢獻是在開發系統過程中的一種新的思維方式:通過url來設計系統的結構。根據REST,每個url都代表一個resource,而整個系統就是由這些resource組成的。是以,如果url是設計良好的,那麼系統的結構就也應該是設計良好的。對于非高手級的開發人員來說,考慮一個系統如何架構總是一個很抽象的問題。靈活開發所提倡的Test Driven Development,其好處之一(我覺得是最大的好處)就是可以通過testcase直覺地設計系統的接口。比如在還沒有建立一個class的時候就編寫一個testcase,雖然設定不能通過編譯,但是testcase中的方法調用可以很好地從class使用者的角度反映出需要的接口,進而為class的設計提供了直覺的表現。這與在REST架構中通過url設計系統結構非常類似。雖然我們連一個功能都沒有實作,但是我們可以先設計出我們認為合理的url,這些url甚至不能連接配接到任何page或action,但是它們直覺地告訴我們:系統對使用者的通路接口就應該是這樣。根據這些url,我們可以很友善地設計系統的結構。

讓我在這裡重申一遍:REST允許我們通過url設計系統,就像Test Driven Development允許我們使用testcase設計class接口一樣。

OK,既然url有這樣的好處,那我們就着重讨論一下如何設計url。網絡應用通常都是有hierarchy的,像棵大樹。我們通常希望url也能反映出資源的層次性。比如對于一個blog應用:/articles表示所有的文章,/articles/1表示id為1的文章,這都比較直覺。遺憾的是,網絡應用的資源結構永遠不會如此簡單。是以人們常常會問這樣一個問題:RESTful的url能覆寫所有的使用者請求嗎?比如,login如何RESTful?search如何RESTful?

從REST的概念上來看,所有可以被抽象為資源的東東都可以使用RESTful的url。是以對于上面的兩個問題,如果login和search可以被抽象為資源,那麼就可以使用RESTful的url。search比較簡單,因為它會傳回搜尋結果,是以可以被抽象為資源,并且隻實作index方法就可以了(隻需要顯示搜尋結果,沒有create、destory之類的東西)。然而這裡面也有一個問題:search的關鍵字如何傳給server?index方法顯然應該使用HTTP GET,這會把關鍵字加到url後面,當然不符合REST的風格。要解決這個問題,可以把每次search看作一個資源,是以要建立create和index方法,create用來在使用者點選“搜尋”按鈕是通過HTTP POST把關鍵字傳給server,然後index則用來顯示搜尋結果。這樣一來,我們還可以記錄使用者的搜尋曆史。使用同樣的方法,我們也可以對login應用REST,即每次login動作是一個資源。

現在,我們來複雜一些的東東。如何用url表達“category為ruby的article”?一開始可能想到的是/category/ruby/articles,這種想法很直覺。但是我覺得裡面的category是不需要的,我們可以直接把“/ruby”了解為“category是ruby”,也就是說“ruby”出現的位置說明了它指的就是category。OK,/ruby/articles,單單從這個url上看,我們能獲得多少關于category的資訊呢?顯然category隐藏在了url後面,這樣做到底好不好,應該是仁者見仁,智者見智了。對于如何表達category這樣的東西,我還沒想出很好的方式,大家有什麼好idea,可以一起讨論。

另外還有一種url形式,它對應到程式中的繼承關系。比如product是一個父類,book和computer是其子類。那麼所有産品的url應該是/products,所有書籍的url應該是/books,所有電腦的url應該是/computers。這一想法就比較直覺了,而且再次驗證了url可以幫助我們進行設計的論點。

讓我再說明一下我的想法:如果每個使用者需求都可以抽象為資源,那麼就可以完全使用REST。

由此看來,使用REST的關鍵是如何抽象資源,抽象得越精确,對REST的應用就越好。是以,如何改變我們目前根深蒂固的基于action的思想是最重要的。

有了對第一個問題的讨論,第二個問題就容易讨論多了。REST會取代MVC嗎?還是彼此是互補關系(就像AOP對于OOP)?答案是It depends!如果我們可以把所有的使用者需求都可以抽象為資源,那麼MVC就可以推出曆史的舞台了。如果情況相反,那麼我們就需要混合使用REST和MVC。

當然,這是非常理想的論斷。可能我們無法找到一種方法可以把所有的使用者需求都抽象為資源,因為保證這種抽象的完整性(即真的是所有需求都可以)需要形式化的證明。而且即使被證明出來了,由于開發人員的能力和喜好不同,MVC肯定也會成為不少人的首選。但是對于希望擁抱REST的人來說,這些都沒有關系。隻要你開發的系統所設計的問題域可以被合理地抽象為資源,那麼REST就會成為你的開發利器。

是以,所有希望擁抱REST的朋友們,趕快訓練自己如何帶上資源的眼鏡看世界吧,這才是REST的核心所在。

轉載自javaeye論壇 作者:AllenYoung

原文位址:http://www.javaeye.com/topic/70113