博客原文傳送門(mén):當(dāng)我們談?wù)揜ESTful時(shí)钧舌,我們?cè)谡務(wù)撌裁?/a>???
標(biāo)題好像有點(diǎn)噱頭,山人在此先檢討涎跨。去知乎了一下洼冻,發(fā)現(xiàn)這種句式叫做卡佛句式,好像村上春樹(shù)也用過(guò)隅很,看來(lái)是雅俗共賞的撞牢。且回正文,最近Team要做一個(gè)Service叔营,負(fù)責(zé)一些全局資源的管理屋彪,并為Internal的其他Services提供通用資源訪問(wèn)接口。想著之前一直是用一些RPC-style的架構(gòu)绒尊,不如來(lái)試試RESTful的吧畜挥。雖然關(guān)于什么才是真正的REST一直有很多爭(zhēng)論。
I- Web服務(wù)的架構(gòu)風(fēng)格
就像面向?qū)ο笈c面向過(guò)程編程模型一樣垒酬,Web Service應(yīng)用服務(wù)中也有兩種典型的架構(gòu)模式砰嘁,Remote Procedure Call(即,RPC勘究,還有一種類(lèi)似的叫法Remote Method Invocation細(xì)微的不同之處在于矮湘,RPC通過(guò)調(diào)用遠(yuǎn)程的過(guò)程函數(shù),而RMI側(cè)重于通過(guò)獲取遠(yuǎn)程的對(duì)象引用口糕,而通過(guò)對(duì)象引用調(diào)用其內(nèi)部的方法缅阳。)與REpresentational State Transfer(即REST)。通常RPC是Operation Oriented景描,其通過(guò)暴露內(nèi)部的操作接口十办,提供與之相應(yīng)的不同編程語(yǔ)言的Client API來(lái)實(shí)現(xiàn)客戶端與服務(wù)器的交互。通常與服務(wù)器的通信基于HTTP協(xié)議超棺,但主要利用其作為一種傳輸媒介向族,把通訊的方法,信息以及狀態(tài)都封裝在HTTP協(xié)議的URI或是Body內(nèi)部棠绘。且大多使用GET或是POST等單一方法件相。而REST則是一種Resource Oriented架構(gòu),就像面向?qū)ο缶幊桃粯友醪裕浒岩粋€(gè)對(duì)象實(shí)體視為一種數(shù)據(jù)資源夜矗,針對(duì)每一種數(shù)據(jù)資源都會(huì)有相應(yīng)的Service Endpoint,其也用HTTP協(xié)議让虐,但不同于RPC紊撕,REST充分利用了HTTP協(xié)議在WWW上取得的成功,直接視HTTP協(xié)議為一種應(yīng)用層協(xié)議赡突,重用其各種HTTP方法对扶,以及統(tǒng)一響應(yīng)碼定義区赵,為全網(wǎng)范圍內(nèi)分布式系統(tǒng)通信提供了統(tǒng)一規(guī)范標(biāo)準(zhǔn)。
嚴(yán)格來(lái)說(shuō)REST本身并不是一種具體架構(gòu)辩稽,而是一種更高level的標(biāo)準(zhǔn)規(guī)范惧笛。初見(jiàn)于Roy Fielding的論文中,文中試圖提出一種針對(duì)分布式超媒體(Hypermedia)系統(tǒng)架構(gòu)逞泄,具有豐富表達(dá)能力的應(yīng)用程序狀態(tài)轉(zhuǎn)換(即REST)Web應(yīng)用服務(wù)的軟件工程設(shè)計(jì)原則患整。以及依照這些原則下,規(guī)定客戶端與服務(wù)器交互時(shí)應(yīng)該遵守的一些限制喷众。比如客戶端與服務(wù)器的交互需是無(wú)狀態(tài)的各谚;為了提高Service性能而引入緩存機(jī)制;為了簡(jiǎn)化整個(gè)系統(tǒng)架構(gòu)到千,提高交互的可見(jiàn)性提出的Uniform Interface的限制等論文中主要有如下constrains:
昌渤。論文是在2000年寫(xiě)的,開(kāi)創(chuàng)性和預(yù)見(jiàn)性可想而知憔四。而正是因?yàn)槠洳皇蔷唧w的某個(gè)實(shí)現(xiàn)架構(gòu)膀息,也不是真正如RFC那樣的定義清晰的業(yè)界標(biāo)準(zhǔn)。這么多年來(lái)了赵,對(duì)REST的解讀自然是仁者見(jiàn)仁智者見(jiàn)智了潜支。
隨著近幾年RESTful Service越來(lái)越熱,幾乎每一個(gè)人在設(shè)計(jì)Web Service架構(gòu)時(shí)都聲稱(chēng)自己是遵照REST原則來(lái)設(shè)計(jì)的柿汛。因而誕生了好多RESTful Service API冗酿。但其實(shí)大多數(shù)系統(tǒng)的設(shè)計(jì)都是不倫不類(lèi)的RPC與REST混合體。正如Richardson在他的那本經(jīng)典的RESTful Web Services中所說(shuō)络断,其實(shí)多數(shù)人設(shè)計(jì)的都是RESTful-RPC混合架構(gòu)模式裁替。但既然我們的主要爭(zhēng)論點(diǎn)在RPC和REST這兩者身上,不妨通過(guò)特征比較法來(lái)看看二者的主要不同點(diǎn)貌笨。
II- RPC vs REST
RPC和REST都是基于HTTP協(xié)議的弱判,我們不妨先看看他們使用HTTP請(qǐng)求的方式有什么不同。當(dāng)Client發(fā)起一個(gè)基本的HTTP請(qǐng)求锥惋,首先需要確定兩個(gè)內(nèi)容昌腰,即
HTTP Verb(Method)
URI(Resources Endpoint)
1- HTTP Verb
對(duì)于RPC而言,常用的HTTP方法只是POST和GET净刮,尤其對(duì)于POST的使用比較依賴剥哑。且很多時(shí)候決定是使用GET還是POST的主要依據(jù)就是是否請(qǐng)求含有數(shù)據(jù)硅则。而對(duì)于經(jīng)典的RPC架構(gòu)的產(chǎn)物淹父,如XML-RPC或是SOAP-RPC,由于關(guān)于調(diào)用服務(wù)的所有信息怎虫,如操作類(lèi)型暑认,數(shù)據(jù)等都被封裝在XML或是SOAP協(xié)議體Envelope內(nèi)困介,而該協(xié)議本身又只被作為HTTP協(xié)議體封裝傳輸。
XML RPC示例代碼:
POST /RPC2 HTTP/1.0User-Agent: Frontier/5.1.2 (WinNT)Host: betty.userland.comContent-Type: text/xmlContent-length: 181examples.getStateName41
SOAP RPC示例代碼:
POST /StockQuote HTTP/1.1Host: www.stockquoteserver.comContent-Type: text/xml; charset="utf-8"Content-Length: nnnnSOAPAction: "Some-URI"DIS
我們來(lái)分析下該SOAP請(qǐng)求蘸际,其目的很簡(jiǎn)單座哩,就是得到上次的交易價(jià)格,其Operation是SOAP體中的GetLastTradePrice方法粮彤,從方法名我們不難看出根穷,其實(shí)就是一個(gè)簡(jiǎn)單的查詢。但是卻封裝成復(fù)雜的SOAP協(xié)議體导坟,且本來(lái)基于HTTP協(xié)議的查詢請(qǐng)求屿良,GET語(yǔ)義就能夠滿足,但其卻用POST方法來(lái)做惫周。我們知道從HTTP協(xié)議的角度尘惧,各個(gè)HTTP Verb都有其標(biāo)準(zhǔn)規(guī)范,如下圖所示:
即POST請(qǐng)求通常用在資源創(chuàng)建或更新時(shí)递递,而GET則是用在讀取一個(gè)資源喷橙。這樣做的一個(gè)好處就是HTTP協(xié)議是整個(gè)WWW都一致使用的協(xié)議,全網(wǎng)范圍內(nèi)有著一致的上下文登舞,而不像SOAP示例中其是讀取資源還是更新資源只有客戶端和服務(wù)器自己知道贰逾。統(tǒng)一HTTP Verb帶來(lái)的另一個(gè)好處就是,Service緩存機(jī)制的實(shí)現(xiàn)變得很簡(jiǎn)單逊躁。因?yàn)榇蠹叶冀⒃谶@些Verb規(guī)范的共識(shí)之上似踱,可以直接通過(guò)請(qǐng)求方法來(lái)判斷是否這些內(nèi)容該緩存或者應(yīng)該從緩存讀取。
除了HTTP Verb稽煤,REST也基于統(tǒng)一的HTTP Response Code核芽,來(lái)表示請(qǐng)求響應(yīng)的狀態(tài)。把HTTP響應(yīng)頭的語(yǔ)義狀態(tài)與應(yīng)用本身的資源請(qǐng)求結(jié)果狀態(tài)統(tǒng)一酵熙。如Amazon S3 REST風(fēng)格的響應(yīng)碼:
便不會(huì)再像RPC風(fēng)格的響應(yīng)那樣轧简,響應(yīng)碼是200/OK,但返回的響應(yīng)體卻是Service specific的錯(cuò)誤碼匾二。完全的HTTP響應(yīng)碼標(biāo)準(zhǔn)定義可以參考HTTP/1.1協(xié)議也有人是這么記的:(
哮独。
2- URI
利用統(tǒng)一的HTTP Verb, Response Code,也使得Fielding提出的Uniform interface成為可能察藐。另一方面皮璧,網(wǎng)絡(luò)上的資源訪問(wèn)統(tǒng)一接口便是URI,對(duì)于Resource-Oriented架構(gòu)模式而言分飞,便需要一個(gè)URI能夠清晰無(wú)誤的描述一個(gè)資源對(duì)象悴务。且URI應(yīng)該有層次結(jié)構(gòu),其改變最好能夠以一種客戶端用戶可預(yù)測(cè)的方式來(lái)進(jìn)行。按照Richardson的說(shuō)法讯檐,一個(gè)好的URI需要self-descriptive和Addressibility羡疗。
與之相反,如前面提到的别洪,RPC架構(gòu)風(fēng)格往往提供單個(gè)Service的全局URI叨恨,然后通過(guò)SOAP等應(yīng)用協(xié)議來(lái)封裝方法和所要獲取的資源。
3- Stateless
Resource-Oriented架構(gòu)的另一個(gè)重要特征便是無(wú)狀態(tài)性挖垛。但就Web
Service而言痒钝,狀態(tài)有兩種,一種是應(yīng)用程序狀態(tài)痢毒,一種是資源狀態(tài)午乓。資源狀態(tài)是Service本身內(nèi)部維持的狀態(tài),其通過(guò)統(tǒng)一資源接口暴露給
Client闸准。當(dāng)Client操作一個(gè)資源時(shí)益愈,如在一個(gè)圖片分享網(wǎng)站上上傳了一幅照片,那么從Service的角度夷家,這個(gè)資源新產(chǎn)生了蒸其,可以給它賦予一個(gè)
URI來(lái)讓Client進(jìn)行增刪改等資源操作了。這些都是Service需要維護(hù)的狀態(tài)库快。應(yīng)用程序狀態(tài)是指客戶端的行為摸袁。如其訪問(wèn)了分頁(yè)資源,其當(dāng)前所在
的頁(yè)數(shù)义屏,之前訪問(wèn)的是哪兒靠汁,需不需要再次認(rèn)證。這些都屬于應(yīng)用程序狀態(tài)闽铐。REST的風(fēng)格就是蝶怔,我不會(huì)維持這些狀態(tài)。所有的狀態(tài)信息需要Client自己提
供兄墅。比如認(rèn)證踢星,客戶端需要每次訪問(wèn)時(shí)都提供認(rèn)證。當(dāng)然這有利有弊隙咸,但使得Web Service變得簡(jiǎn)單沐悦。
III- RESTful該如何評(píng)價(jià)
前面一章我們?cè)噲D通過(guò)對(duì)比的方法來(lái)理解REST架構(gòu),但我們?cè)撊绾味x一個(gè)Web Services是RESTful的五督,或不是RESTful的呢藏否?還是Richardson這哥么,他把REST的一些原則特性細(xì)化成一系列可執(zhí)行的步驟充包,提出了自己的REST成熟度模型副签。并再一次QCon的演講中把它形象化為REST榮耀之路:
圖中把REST成熟度分為0-3級(jí)。
第0級(jí)是指沒(méi)有使用REST,仍然淹沒(méi)在RPC的人流中继薛。把HTTP協(xié)議當(dāng)做一種跟Web上遠(yuǎn)程系統(tǒng)交互的傳輸機(jī)制,所謂的POX(Plain Old XML)指的就是與SOAP愈捅,XML遏考,JSON RPC類(lèi)似的機(jī)制。
第1級(jí)是指引入了Resource的概念蓝谨。交互的方式不再是單一的Endpoint下的傳輸體中不同方法的封裝灌具,而是能夠針對(duì)每一個(gè)獨(dú)立的資源實(shí)體,有相應(yīng)的URI譬巫。我們的操作方法不再是零散的參數(shù)傳遞咖楣,而是在這個(gè)資源實(shí)體上的方法調(diào)用。
第2級(jí)是指引入了我們前面提到的HTTP Verb和Response Code芦昔。我們不依賴于傳入自定義的操作方法诱贿,而是利用HTTP協(xié)議本身的優(yōu)勢(shì)。當(dāng)我們想查詢一個(gè)資源時(shí)候用GET咕缎,想更新一個(gè)資源時(shí)用PUT珠十,想創(chuàng)建一個(gè)資源時(shí)用POST關(guān)于POST和PUT的差別,統(tǒng)一的觀點(diǎn)是:當(dāng)Client知道待更新資源的URI凭豪,就用PUT焙蹭。當(dāng)Client不知道待更新資源的URI,只是把資源給Service嫂伞,讓它來(lái)決定資源的URI孔厉,就用POST。
帖努。
第3級(jí)就是Fielding所說(shuō)的Hypertext As the Enginee of Application State(HATEOAS)撰豺。也就指示了State Transfer的含義。通過(guò)Hypermedia的響應(yīng)來(lái)引導(dǎo)Client的下一步行為拼余。當(dāng)Client能夠以一種可以預(yù)測(cè)的方式來(lái)探索Service的調(diào)用接口郑趁。更詳細(xì)的介紹可以參考末尾的引用。
當(dāng)然這個(gè)成熟度模型可以很好的表達(dá)REST姿搜,但并不是唯一的標(biāo)準(zhǔn)寡润,也有很多人提出了質(zhì)疑和自己的看法。但山人認(rèn)為舅柜,它至少能夠幫助我們以一種邏輯的方式讓我們對(duì)什么是RESTful有了更為深入和清晰的理解梭纹。
So long, and thanks for all the fish!
參考
[1].Do-You-Really-Know-Why-You-Prefer-Rest-over-Rpc.
[2].Rest Arch Style
[3].Measuring-Rest
[4].Rest in Practice