Richardson 成熟模型

原文地址: http://martinfowler.com/articles/richardsonMaturityModel.html

Richardson 成熟模型

通往REST榮光的步驟

一個將rest做法的基本原則元素分成3個步驟的模型(Leonard Richardson發(fā)明的).這些介紹了資源(resources),http動作和超媒體控制(hypermedia controls).



最近我在讀<Rest In Practice>(一本我?guī)讉€同事最近在寫的書)的草稿.他們的目的是解釋如何用rest網(wǎng)絡(luò)服務(wù)去解決許多企業(yè)會遇到的結(jié)合問題.這本書的中心思想是一個概念:網(wǎng)絡(luò)是一個大量大規(guī)模的分布系統(tǒng)工作地很好的存在性證明.因此我們可以利用這個思想的一些主意去更簡單地建立集合系統(tǒng).


圖一:通往REST的步驟

為了說明一個網(wǎng)絡(luò)風(fēng)格的系統(tǒng)的特殊屬性,這些作者用了一個Leonard Richardson發(fā)明的Rest成熟模型和在一次QCon談話上的解釋.這個模型用簡潔的方法去思考利用這三個工具,因此我想嘗試一個自己的解釋.(這些協(xié)議的例子只是用來說明,我不覺得它們值得寫成代碼并進(jìn)行測試,因此在細(xì)節(jié)上會存在許多問題)


等級0

這個模型的起點是用http作為一個遠(yuǎn)程交互的傳輸系統(tǒng),但是不用任何網(wǎng)絡(luò)的機(jī)制.基本上你在這里做的就是把HTTP作為你遠(yuǎn)程交互系統(tǒng)的通道機(jī)制,一般基于遠(yuǎn)程程序調(diào)取(Remote Procedure Invocation).


圖表2:等級0的一個例子

讓我們假設(shè)我希望和我的醫(yī)生預(yù)約一次約定.我的預(yù)約軟件首先需要知道我的醫(yī)生在給定日期的開放位置,因此我發(fā)起一個請求到醫(yī)院的約定系統(tǒng)獲得這個信息.在等級0的場景中,醫(yī)院將公開一個服務(wù)端點到一些URI上.我接著post到那個端點一個文檔包含我的請求的細(xì)節(jié).

POST /appointmentService HTTP/1.1

[various other headers]

<openSlotRequest date = "2010-01-04" doctor = "mjones" />

這個服務(wù)器將返回一個文檔給我以下的信息:

HTTP/1.1 200 OK

[various headers]

<openSlotList>

? ? <slot start = "!400" end = "1450" >

? ? ? ? <doctor id = "mjones" />

? ? </slot>

? ? <slot start = "1600" end = " 1650">

? ? ? ? <doctor id = "mjones" />

? ? </slot>

</openSlotList>

我的例子用的是XML,但是內(nèi)容可以是任何格式的:JSON,YAML,鍵值對或者任何自定義的類型.

我下一步將要定一個約定,我也是post一個文檔到那個端點.

POST /appointmentService HTTP/1.1

[various other headers]

<appointmentRequest>

? ? </slot doctor = "mjones" start = "1400" end = "1450" />

? ? <patient id = "jsmith" />

</appointmentRequest>

如果一切順利我將得到一個回復(fù)說我已經(jīng)約定成功了.

HTTP/1.1 200 OK

[various headers]

<appointment>

? ? <slot doctor = "mjones" start = "1400" end = "1450" />

? ? <patient id = "jsmith" />

</appointment>

如果這中間有問題,比如有人在我之前約定了,那么我將獲得一些錯誤信息在回復(fù)的正文里.

HTTP/1.1 200 OK

[various headers]

<appointmentRequestFailure>

? ? <slot doctor = "mjones" start = "1400" end = "1450" />

? ? <patient id = "jsmith" />

? ? <reason>Slot not available</reason>

</appointmentRequestFailure>

到目前為止這是一個直截了當(dāng)?shù)腞PC風(fēng)格的系統(tǒng).這很簡單因為這僅僅是平常老套的XML來回傳遞.如果你用SOAP或者XML-RPC,基本是一樣的機(jī)制,唯一的區(qū)別就是你將XML格式的信件用某種格式的信封封裝了起來.


等級1

在RMM中通往Rest的榮光的第一步即是介紹資源.因此現(xiàn)在我們不再讓我們的請求到單一的網(wǎng)絡(luò)端點,而是開始和獨(dú)立的資源交流.


圖表3:加入資源

因此對于我們的初始請求,我們可能有一個給定醫(yī)生的資源.

POST /doctors/mjones HTTP/1.1

[various other headers]

<openSlotRequest date = "2010-01-04" />

這個請求包含了一些基本的信息,但是每一個開放時間現(xiàn)在成為了一個被強(qiáng)調(diào)的獨(dú)立資源.

HTTP/1.1 200 OK

[various headers]

<openSlotList>

? ? <slot id = "1234" doctor = "mjones" start = "1400" end = "1450" />

? ? <slot id = "5678" doctor = "mjones" start = "1600" end = "1650" />

</openSlotList>

用特定的資源去預(yù)訂意味著要post到特定的資源.

POST /slots/1234 HTTP/1.1

[various other headers]

<appointmentRequest>

? ? <patient id = "jsmith" />

</appointmentRequest>

如果一切順利,那么將得到和之前類似的回復(fù).

HTTP/1.1 200 OK

[various headers]

<appointment>

? ?<slot id = "1234" doctor = "mjones" start = "1400" end = "1450" />

? ?<patient id = "jsmith"/>

</appointment>

不同之處在于如果有任何人需要對預(yù)約做任何事,類似于定一些測試,他們首先要get到約定這一個資源,可能通過一個類似于http://royalhope.nhs.uk/slots/1234/appointment的URI,并且post到那個資源.

對于像我這樣的對象男士這類似于對象的識別的表達(dá).不是調(diào)用方法傳遞參數(shù),我們提供額外信息的參數(shù)去調(diào)用關(guān)于某個特定對象的方法.


等級2-HTTP動作

我已經(jīng)在等級0和等級1的所有交互動作用了post動作,但是有些人使用get動作代替或者增加get動作.在那些等級這沒有啥區(qū)別,它們都是被用作成通道機(jī)制運(yùn)行你通過HTTP進(jìn)行交互.等級2要駛離這一點了,運(yùn)用HTTP動作盡可能和HTTP本身運(yùn)用這些動作一樣.


圖表4:等級2-加入HTTP動作

對于我們的開發(fā)時間列表,這意味著我們希望使用get動作.

GET /doctors/mjones/slots?date=20100104&status=open HTTP/1.1

Host: royalhope.nhs.uk

回復(fù)和之前用post動作時的回復(fù)一樣

HTTP/1.1 200 OK

[various headers]

<openSlotList>

? ? <slot id = "1234" doctor = "mjones" start = "1400" end = "1450" />

? ? <slot id = "5678" doctor = "mjones" start = "1600" end = "1650" />

</openSlotList>

在等級2,在一個類似這個的請求中用get動作是很決斷的.HTTP將get定義為了一個很安全的操作,它不會造成顯著的改變狀態(tài)或之類的事情.這允許我們調(diào)用get動作,無論是多少次,無論什么順序,都會返回同樣的結(jié)果.一個重要的結(jié)果就是這允許任何請求的路由可以使用緩存,這是使得網(wǎng)絡(luò)表現(xiàn)地這么好的關(guān)鍵.HTTP有多種方法來支持緩存,這都能用于所有的通訊中.遵循HTTP的規(guī)則使得我們能夠利用這種能力的優(yōu)勢.

去定一個約定我們需要能改變狀態(tài)的HTTP動作,一個post動作或者put動作.我將和之前一樣用post動作.

POST /slots/1234 HTTP/1.1

[various other headers]

<appointmentRequest>

? ? <patient id = "jsmith" />

</appointmentRequest>

運(yùn)用post還是put的優(yōu)劣勢比我這里能講到的要更復(fù)雜深入,也許我會再寫一篇文章談這一點.但是我想指出一些人沒能正確理清POST/PUT和create/update之前的對應(yīng)關(guān)系.他們之前的區(qū)別和那不同.

盡管我在等級1運(yùn)用了同樣的post,那里還有一個顯著的區(qū)別關(guān)于遠(yuǎn)程服務(wù)是如何回應(yīng)的,如果一切順利,服務(wù)器將返回201碼以表明一個新的資源在這個世界上創(chuàng)建了.

HTTP/1.1 201 Created

Location: slots/1234/appointment

[various headers]

<appointment>

? ? <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>

? ? <patient id = "jsmith"/>

</appointment>

這個201回復(fù)包含了一個URI位置特性,因此客戶可以利用它去GET未來資源現(xiàn)在的狀態(tài).這個回復(fù)同時也含有一個那資源的表現(xiàn),可以替用戶節(jié)省一個額外的請求.

當(dāng)時期出現(xiàn)錯誤時,這里還有個不同,比如另一個人在定這個會話.

HTTP/1.1 409 Conflict

[various headers]

<openSlotList>

? ? <slot id = "5678" doctor = "mjones" start = "1600" end = "1650"/>

</openSlotList>

這個回復(fù)重要的一部分是用HTTP狀態(tài)碼預(yù)示有些事出錯了.比如是409是不錯的選擇去預(yù)示有人已經(jīng)很矛盾地更新了這個資源.與其用200的返回碼卻包含一個錯誤信息,在等級2我們曾經(jīng)明確地運(yùn)用過這類的錯誤返回.這取決于協(xié)議設(shè)計者去決定用什么狀態(tài)碼,但是如果出現(xiàn)了錯誤不應(yīng)該是2xx系列的回復(fù).等級2介紹了HTTP動作和HTTP狀態(tài)碼.

這里有個不一致的前行.REST貢獻(xiàn)者談到了用所有的HTTP動作.他們也在證明他們說的REST試圖學(xué)習(xí)網(wǎng)絡(luò)的部分成功.但是全世界范圍的網(wǎng)絡(luò)在實踐中不怎么使用PUT和POST結(jié)構(gòu).這有許多理由去多用PUT和PATCH,這里證明現(xiàn)有的網(wǎng)絡(luò)不是其中之一.

被現(xiàn)在網(wǎng)絡(luò)的存在性所支持的關(guān)鍵元素是在安全元素和非安全元素間的分離.結(jié)合在一起就是用狀態(tài)碼去幫助交流遇到的錯誤.


等級3-超媒體控制

最后一個等級將解釋一些你經(jīng)常聽到在丑陋的首字母縮寫詞HATEOAS(超文本作為應(yīng)用狀態(tài))下面的一些事情.它強(qiáng)調(diào)了如何從得到開放時間列表到知道如何預(yù)約的問題.


圖表5:等級3-加入超媒體控制

我們從和我們在等級2發(fā)送的同樣的get請求開始.

GET /doctors/mjones/slots?date=20100104&status=open HTTP/1.1

Host: royalhope.nhs.uk

但是返回加上了一些新元素.

HTTP/1.1 200 OK

[various headers]

<openSlotList>

? ? <slot id = "1234" doctor = "mjones" start = "1400" end = "1450">

? ? ? ? <link rel = "/linkrels/slot/book" uri = "/slots/1234" />

? ? </slot>

? ? <slot id = "5678" doctor = "mjones" start = "1600" end = "1650">

? ? ? ? <link rel = "/linkrels/slot/book" uri = "/slots/5678"/>

? ? </slot>

</openSlotList>

每一個開放時間有一個鏈接元素包含了一個uri來告訴我們?nèi)绾稳ヮA(yù)訂一個約定.

超媒體的關(guān)鍵是它們告訴了我們下一步可以做什么和可以操作的資源的URI.不要求我們必須知道如何去post我們的預(yù)約請求,回復(fù)中的超媒體控制告訴了我們?nèi)绾稳プ?

從等級2拷貝下來的post請求:

POST /slots/1234 HTTP/1.1

[various other headers]

<appointmentRequest>

? ? <patient id = "jsmith"/>

</appointmentRequest>

回復(fù)包含了一些超媒體控制關(guān)于下一步可以做的不同事情.

HTTP/1.1 201 Created

Location: http://royalhope.nhs.uk/slots/1234/appointment

[various headers]

<appointment>

? ? <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>

? ? <patient id = "jsmith" />

? ? <link rel = "/linkrels/appointment/cancel" uri = "/slots/1234/appointment"/>

? ? <link rel = "/linkrels/appointment/addTest" uri = "/slots/1234/appointment/tests"/>

? ? <ink rel = "self" uri = "/slots/1234/appointment"/>

? ? <link rel = "/linkrels/appointment/changeTime" uri = "/doctors/mjones/slots?date=20100104@status=open"/>

? ? <link rel = "/linkrels/appointment/updateContactInfo" uri = "/patients/jsmith/contactInfo"/>

? ? <link rel = "/linkrels/help" uri = "/help/appointment"/>

</appointment>

超媒體控制的一個很明顯的優(yōu)點在于允許服務(wù)器改變其URI計劃而不破壞客戶端.只要客戶查找"addTest"鏈接的URI,接下來服務(wù)端可以改變所有的URI而不是始終用初始的入口.

一個更遠(yuǎn)的受益處在于它幫助了客戶端的開發(fā)者探索協(xié)議.這些鏈接給了客戶端的開發(fā)者一個下一步可能需要做什么的提示.它沒有給所有的信息:"latest"和"cancel"控制都指向同一個URI-他們需要弄清楚其中一個是Get動作另一個是Delete動作.但是這至少給了他們一個起點去思考更多的信息和去協(xié)議文檔看類似的URI.

類似的,它允許了服務(wù)端組去提倡新的能力通過放一個鏈接到回復(fù)中去.如果客戶端的開發(fā)者關(guān)注這些未知的新鏈接,這些鏈接會觸發(fā)他們未來新的探索.

這里沒有絕對的標(biāo)準(zhǔn)如何表示超媒體控制.我這里做的只是用了<<Rest In Practice>>組目前的建議,他們服從了ATOM(RFC 4287).我用了一個包含為了特定URI的uri屬性和描述關(guān)系的ref屬性的link元素.一個廣為人知的關(guān)系是赤裸的(比如self用了指向元素自己),任何為那個服務(wù)的明確都是一個完全合格的URI.ATOM狀態(tài)定義了很有名的linkref是一種鏈接關(guān)系的注冊.我寫的這些受到ATOM可以做什么的限制,這在等級3restfuless中被看作成一個領(lǐng)導(dǎo)者.


這些等級的意義

我本應(yīng)強(qiáng)調(diào)RMM盡管是一個很好的方式去思考REST的元素,卻不是一種REST本身的等級定義.Roy Fielding在<<level 3 RMM is a pre-condition of REST>>中說的很清楚了.像在軟件業(yè)的許多團(tuán)隊一樣,REST有許多定義.但是因為Roy Fielding創(chuàng)造了這個術(shù)語,他的定義應(yīng)該比其他的更重要一些.

我發(fā)現(xiàn)RMM有用之處在于其提供了一個一步步的方法去理解在restful思考背后的基本想法.從這點來說,我把它看作一個幫助我們學(xué)習(xí)概念的工具,而不是我們應(yīng)該在任務(wù)機(jī)制中必須用的東西.我不確定目前我們有了足夠的例子來確定restful做法是結(jié)合系統(tǒng)的正確做法.我真心覺得其是一種很有吸引力的做法以及我會在很多情況下推薦這種做法.

和Ian Robinson談起這點,他強(qiáng)調(diào)當(dāng)Leonard Richardson第一次提出這個模型時他發(fā)現(xiàn)非常有吸引力之處在于他和普遍的設(shè)計技術(shù)的聯(lián)系.

- 等級1 利用分治去解決了處理復(fù)雜性的問題,將大規(guī)模的服務(wù)終點打碎成多個資源.

- 等級2 引入了標(biāo)準(zhǔn)的動作集合,因此我們可以用同樣的方法處理類似的問題,移除不必要的改變.

- 等級3 引入了可發(fā)現(xiàn)性,提供了一種使得協(xié)議自己就是文檔的方法.

結(jié)果是一種模型幫助我們思考我們想要提供的HTTP服務(wù)的種類和框起人們對和其交互的期望.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末与倡,一起剝皮案震驚了整個濱河市何乎,隨后出現(xiàn)的幾起案子勤庐,更是在濱河造成了極大的恐慌枫浙,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)僻焚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來膝擂,“玉大人虑啤,你說我怎么就攤上這事〖懿觯” “怎么了狞山?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長叉寂。 經(jīng)常有香客問我萍启,道長,這世上最難降的妖魔是什么屏鳍? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任勘纯,我火速辦了婚禮,結(jié)果婚禮上钓瞭,老公的妹妹穿的比我還像新娘驳遵。我一直安慰自己,他們只是感情好山涡,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布堤结。 她就那樣靜靜地躺著,像睡著了一般鸭丛。 火紅的嫁衣襯著肌膚如雪竞穷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天鳞溉,我揣著相機(jī)與錄音瘾带,去河邊找鬼。 笑死穿挨,一個胖子當(dāng)著我的面吹牛月弛,可吹牛的內(nèi)容都是我干的肴盏。 我是一名探鬼主播科盛,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼帽衙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了贞绵?” 一聲冷哼從身側(cè)響起厉萝,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎榨崩,沒想到半個月后谴垫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡母蛛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年翩剪,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片彩郊。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡前弯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出秫逝,到底是詐尸還是另有隱情恕出,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布违帆,位于F島的核電站浙巫,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏刷后。R本人自食惡果不足惜的畴,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望尝胆。 院中可真熱鬧丧裁,春花似錦、人聲如沸班巩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抱慌。三九已至逊桦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間抑进,已是汗流浹背强经。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留寺渗,地道東北人匿情。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓兰迫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親炬称。 傳聞我的和親對象是個殘疾皇子汁果,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)玲躯,斷路器据德,智...
    卡卡羅2017閱讀 134,651評論 18 139
  • 一說到REST,我想大家的第一反應(yīng)就是“啊跷车,就是那種前后臺通信方式棘利。”但是在要求詳細(xì)講述它所提出的各個約束朽缴,以及如...
    時待吾閱讀 3,423評論 0 19
  • API定義規(guī)范 本規(guī)范設(shè)計基于如下使用場景: 請求頻率不是非常高:如果產(chǎn)品的使用周期內(nèi)請求頻率非常高善玫,建議使用雙通...
    有涯逐無涯閱讀 2,533評論 0 6
  • 從今天開始,我開始學(xué)習(xí)Retrofit密强,整體Retrofit內(nèi)容如下: 1茅郎、Retrofit解析1之前哨站——理解...
    隔壁老李頭閱讀 6,112評論 4 46
  • 我只是想記錄一下生活。 大一第二學(xué)期了誓斥,我以前總以為上了大學(xué)會比較輕松只洒,事實卻告訴你,沒有什么事很輕松劳坑。 今天和舍...
    木穰閱讀 199評論 1 1