Calvin's Tech Space

          成于堅(jiān)忍,毀于浮躁

             :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理

          這陣子正打算用Rails做個(gè)東東,所以開始系統(tǒng)地學(xué)習(xí)起了Rails。巧合的是,大概兩周前,dlee邀請我加入Fielding博士關(guān)于REST的那篇論文的翻譯團(tuán)隊(duì)。可以說RailsREST這兩個(gè)最熱門的詞匯幾乎同時(shí)擠入了我的生活。隨著我對(duì)Rails的學(xué)習(xí)和對(duì)[Fielding]的翻譯,我也開始對(duì)REST產(chǎn)生了一些不太成熟的想法,寫在這里與大家分享,同時(shí)也起到拋磚引玉的作用,歡迎大家討論。

          先復(fù)習(xí)一下REST的基本思想。[Fielding]REST形式化地定義為一種架構(gòu)風(fēng)格(architecture style),它有架構(gòu)元素(element)和架構(gòu)約束(constraint)組成。這些概念比較晦澀難懂,而且我們做工程的往往并不需要形而上的理解。我們只知道,REST是一種針對(duì)網(wǎng)絡(luò)應(yīng)用的設(shè)計(jì)和開發(fā)方式,可以降低開發(fā)的復(fù)雜性,提高系統(tǒng)的可伸縮性。REST提出了一些設(shè)計(jì)概念和準(zhǔn)則:

          網(wǎng)絡(luò)上的所有事物都被抽象為資源(resource); 

          每個(gè)資源對(duì)應(yīng)一個(gè)唯一的資源標(biāo)識(shí)(resource identifier); 

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

          對(duì)資源的各種操作不會(huì)改變資源標(biāo)識(shí); 

          所有的操作都是無狀態(tài)的(stateless)。 

          對(duì)于當(dāng)今最常見的網(wǎng)絡(luò)應(yīng)用來說,resource identifierurlgeneric connector interfaceHTTP,第4條準(zhǔn)則就是我們常說的url不變性。這些概念中的resouce最容易使人產(chǎn)生誤解。resouce所指的并不是數(shù)據(jù),而是數(shù)據(jù)+特定的表現(xiàn)形式(representation),這也是為什么REST的全名是Representational State Transfer的原因。舉個(gè)例子來說,本月賣得最好的10本書你最喜歡的10本書在數(shù)據(jù)上可能有重疊(有一本書即賣得好,你又喜歡),甚至完全相同。但是它們的representation不同,因此是不同的resource

          REST之所以能夠簡化開發(fā),是因?yàn)槠湟氲募軜?gòu)約束,比如Rails 1.2中對(duì)REST的實(shí)現(xiàn)默認(rèn)把controller中的方法限制在7個(gè):indexshowneweditcreateupdatedestory,這實(shí)際上就是對(duì)CURD的實(shí)現(xiàn)。更進(jìn)一步講,Rails(也是當(dāng)今大部分網(wǎng)絡(luò)應(yīng)用)使用HTTP作為generic connector interfaceHTTP則把對(duì)一個(gè)url的操作限制在了4個(gè)之內(nèi):GETPOSTPUTDELETE

          REST之所以能夠提高系統(tǒng)的可伸縮性,是因?yàn)樗鼜?qiáng)制所有操作都是stateless的,這樣就沒有context的約束,如果要做分布式、做集群,就不需要考慮context的問題了。同時(shí),它令系統(tǒng)可以有效地使用poolREST對(duì)性能的另一個(gè)提升來自其對(duì)clientserver任務(wù)的分配:server只負(fù)責(zé)提供resource以及操作resource的服務(wù),而client要根據(jù)resource中的datarepresentation自己做render。這就減少了服務(wù)器的開銷。

          既然REST有這樣的好處,那我們應(yīng)該義無反顧地?fù)肀。∧壳耙恍┐笈#ㄏ?/font>DHH)都已經(jīng)開始投入到了REST的世界,那我們這些人應(yīng)該做什么或者說思考寫什么你呢?我覺得我們應(yīng)該思考兩個(gè)問題:

          如何使用REST; 

          RESTMVC的關(guān)系。 

          第一個(gè)問題假設(shè)REST是我們應(yīng)該采用的架構(gòu),然后討論如何使用;第二個(gè)問題則要說明REST和當(dāng)前最普遍應(yīng)用的MVC是什么關(guān)系,互補(bǔ)還是取代?

          我們先來談?wù)劦谝粋€(gè)問題,如何使用REST。我感覺,REST除了給我們帶來了一個(gè)嶄新的架構(gòu)以外,還有一個(gè)重要的貢獻(xiàn)是在開發(fā)系統(tǒng)過程中的一種新的思維方式:通過url來設(shè)計(jì)系統(tǒng)的結(jié)構(gòu)。根據(jù)REST,每個(gè)url都代表一個(gè)resource,而整個(gè)系統(tǒng)就是由這些resource組成的。因此,如果url是設(shè)計(jì)良好的,那么系統(tǒng)的結(jié)構(gòu)就也應(yīng)該是設(shè)計(jì)良好的。對(duì)于非高手級(jí)的開發(fā)人員來說,考慮一個(gè)系統(tǒng)如何架構(gòu)總是一個(gè)很抽象的問題。敏捷開發(fā)所提倡的Test Driven Development,其好處之一(我覺得是最大的好處)就是可以通過testcase直觀地設(shè)計(jì)系統(tǒng)的接口。比如在還沒有創(chuàng)建一個(gè)class的時(shí)候就編寫一個(gè)testcase,雖然設(shè)置不能通過編譯,但是testcase中的方法調(diào)用可以很好地從class使用者的角度反映出需要的接口,從而為class的設(shè)計(jì)提供了直觀的表現(xiàn)。這與在REST架構(gòu)中通過url設(shè)計(jì)系統(tǒng)結(jié)構(gòu)非常類似。雖然我們連一個(gè)功能都沒有實(shí)現(xiàn),但是我們可以先設(shè)計(jì)出我們認(rèn)為合理的url,這些url甚至不能連接到任何pageaction,但是它們直觀地告訴我們:系統(tǒng)對(duì)用戶的訪問接口就應(yīng)該是這樣。根據(jù)這些url,我們可以很方便地設(shè)計(jì)系統(tǒng)的結(jié)構(gòu)。

          讓我在這里重申一遍:REST允許我們通過url設(shè)計(jì)系統(tǒng),就像Test Driven Development允許我們使用testcase設(shè)計(jì)class接口一樣。

          OK,既然url有這樣的好處,那我們就著重討論一下如何設(shè)計(jì)url。網(wǎng)絡(luò)應(yīng)用通常都是有hierarchy的,像棵大樹。我們通常希望url也能反映出資源的層次性。比如對(duì)于一個(gè)blog應(yīng)用:/articles表示所有的文章,/articles/1表示id1的文章,這都比較直觀。遺憾的是,網(wǎng)絡(luò)應(yīng)用的資源結(jié)構(gòu)永遠(yuǎn)不會(huì)如此簡單。因此人們常常會(huì)問這樣一個(gè)問題:RESTfulurl能覆蓋所有的用戶請求嗎?比如,login如何RESTfulsearch如何RESTful

          REST的概念上來看,所有可以被抽象為資源的東東都可以使用RESTfulurl。因此對(duì)于上面的兩個(gè)問題,如果loginsearch可以被抽象為資源,那么就可以使用RESTfulurlsearch比較簡單,因?yàn)樗鼤?huì)返回搜索結(jié)果,因此可以被抽象為資源,并且只實(shí)現(xiàn)index方法就可以了(只需要顯示搜索結(jié)果,沒有createdestory之類的東西)。然而這里面也有一個(gè)問題:search的關(guān)鍵字如何傳給serverindex方法顯然應(yīng)該使用HTTP GET,這會(huì)把關(guān)鍵字加到url后面,當(dāng)然不符合REST的風(fēng)格。要解決這個(gè)問題,可以把每次search看作一個(gè)資源,因此要?jiǎng)?chuàng)建createindex方法,create用來在用戶點(diǎn)擊搜索按鈕是通過HTTP POST把關(guān)鍵字傳給server,然后index則用來顯示搜索結(jié)果。這樣一來,我們還可以記錄用戶的搜索歷史。使用同樣的方法,我們也可以對(duì)login應(yīng)用REST,即每次login動(dòng)作是一個(gè)資源。

          現(xiàn)在,我們來復(fù)雜一些的東東。如何用url表達(dá)“categoryrubyarticle”?一開始可能想到的是/category/ruby/articles,這種想法很直觀。但是我覺得里面的category是不需要的,我們可以直接把“/ruby”理解為“categoryruby”,也就是說“ruby”出現(xiàn)的位置說明了它指的就是categoryOK/ruby/articles,單單從這個(gè)url上看,我們能獲得多少關(guān)于category的信息呢?顯然category隱藏在了url后面,這樣做到底好不好,應(yīng)該是仁者見仁,智者見智了。對(duì)于如何表達(dá)category這樣的東西,我還沒想出很好的方式,大家有什么好idea,可以一起討論。

          另外還有一種url形式,它對(duì)應(yīng)到程序中的繼承關(guān)系。比如product是一個(gè)父類,bookcomputer是其子類。那么所有產(chǎn)品的url應(yīng)該是/products,所有書籍的url應(yīng)該是/books,所有電腦的url應(yīng)該是/computers。這一想法就比較直觀了,而且再次驗(yàn)證了url可以幫助我們進(jìn)行設(shè)計(jì)的論點(diǎn)。

          讓我再說明一下我的想法:如果每個(gè)用戶需求都可以抽象為資源,那么就可以完全使用REST

          由此看來,使用REST的關(guān)鍵是如何抽象資源,抽象得越精確,對(duì)REST的應(yīng)用就越好。因此,如何改變我們目前根深蒂固的基于action的思想是最重要的。

          有了對(duì)第一個(gè)問題的討論,第二個(gè)問題就容易討論多了。REST會(huì)取代MVC嗎?還是彼此是互補(bǔ)關(guān)系(就像AOP對(duì)于OOP)?答案是It depends!如果我們可以把所有的用戶需求都可以抽象為資源,那么MVC就可以推出歷史的舞臺(tái)了。如果情況相反,那么我們就需要混合使用RESTMVC

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

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

          轉(zhuǎn)載自javaeye論壇 作者:AllenYoung

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


          資源: http://www.ibm.com/developerworks/cn/web/wa-ajaxarch/

          posted on 2009-09-17 20:06 calvin 閱讀(207) 評(píng)論(0)  編輯  收藏 所屬分類: Java
          主站蜘蛛池模板: 舞阳县| 读书| 新乐市| 门头沟区| 高密市| 城固县| 虞城县| 中超| 田林县| 晋中市| 昭平县| 武隆县| 绥宁县| 潮安县| 施秉县| 三穗县| 涟源市| 丰都县| 德化县| 东明县| 汾西县| 藁城市| 青川县| 北宁市| 隆化县| 汉中市| 平远县| 青冈县| 榆中县| 英山县| 灵丘县| 当雄县| 赤峰市| 宜黄县| 岗巴县| 巴青县| 景宁| 吉水县| 衡阳市| 永仁县| 轮台县|