我所理解的RESTful Web API [设计篇]
《我所理解的RESTful Web API [Web標(biāo)準(zhǔn)篇]》Web服務(wù)已經(jīng)成為了異質(zhì)系統(tǒng)之間的互聯(lián)與集成的主要手段,在過去一段不短的時(shí)間里,Web服務(wù)幾乎清一水地采用SOAP來構(gòu)建。構(gòu)建REST風(fēng)格的Web服務(wù)是最近兩三年風(fēng)行的潮流,所以很多人以為REST是一個(gè)事物。而事實(shí)卻是:REST自其誕生之日起到現(xiàn)在(2014年)已經(jīng)有14年了,它為什么叫這么一個(gè)“奇怪”的名字呢?
目錄?
一、為什么叫這個(gè)“奇怪”的名字?二、采用URI標(biāo)識(shí)資源?
二、采用URI標(biāo)識(shí)資源?
三、使用“鏈接”關(guān)聯(lián)相關(guān)的資源?
四、使用統(tǒng)一的接口?
五、使用標(biāo)準(zhǔn)的HTTP方法?
六、支持多種資源表示方式?
七、無狀態(tài)性
一、為什么叫這個(gè)“奇怪”的名字?
2000年,Roy Thomas Fielding博士在他那篇著名的博士論文《Architectural Styles and the Design of Network-based Software Architectures》中提出了幾種軟件應(yīng)用的架構(gòu)風(fēng)格,REST作為其中的一種架構(gòu)風(fēng)格在這篇論文的第5章中進(jìn)行了概括性的介紹。我個(gè)人建議本書的讀者都能讀讀這篇論文,原文和中文譯文都可以從網(wǎng)絡(luò)上找到。
REST是“REpresentational State Transfer”的縮寫,可以翻譯成“表現(xiàn)狀態(tài)轉(zhuǎn)換”,但是在絕大多數(shù)場(chǎng)合中我們只說REST或者RESTful。為什么會(huì)起這么一個(gè)奇怪的名字呢?我們可以從上述這篇論文中找到答案。Fielding在論文中將REST定位為“分布式超媒體應(yīng)用(Distributed Hypermedia System)”的架構(gòu)風(fēng)格,它在文中提到一個(gè)名為“HATEOAS(Hypermedia as the engine of application state)”的概念。
我們利用一個(gè)面向最終用戶的Web應(yīng)用來對(duì)這個(gè)概念進(jìn)行簡(jiǎn)單闡述:這里所謂的應(yīng)用狀態(tài)(Application State)表示W(wǎng)eb應(yīng)用的客戶端的狀態(tài),簡(jiǎn)單起見可以理解為會(huì)話狀態(tài)。資源在瀏覽器中以超媒體的形式呈現(xiàn),通過點(diǎn)擊超媒體中的鏈接可以獲取其它相關(guān)的資源或者對(duì)當(dāng)前資源進(jìn)行相應(yīng)的處理,獲取的資源或者針對(duì)資源處理的響應(yīng)同樣以超媒體的形式再次呈現(xiàn)在瀏覽器上。由此可見,超媒體成為了驅(qū)動(dòng)客戶端會(huì)話狀態(tài)的轉(zhuǎn)換的引擎。
借助于超媒體這種特殊的資源呈現(xiàn)方式,應(yīng)用狀態(tài)的轉(zhuǎn)換體現(xiàn)為瀏覽器中呈現(xiàn)資源的轉(zhuǎn)換。如果將超媒體進(jìn)一步抽象成一般意義上的資源呈現(xiàn)(Representation )方式,那么應(yīng)用狀態(tài)變成了可被呈現(xiàn)的狀態(tài)(REpresentational State)。應(yīng)用狀態(tài)之間的轉(zhuǎn)換就成了可被呈現(xiàn)的狀態(tài)裝換(REpresentational State Transfer),這就是REST。
REST在我看來是一種很籠統(tǒng)的概念,它代表一種架構(gòu)風(fēng)格。對(duì)于多個(gè)Web應(yīng)用采用的架構(gòu),我們只能說其中某一個(gè)比其它的更具有REST風(fēng)格,而不能簡(jiǎn)單粗暴地說:“它采用了REST架構(gòu)而其它的沒有”。為了將REST真正地落地,Lenoard Rechardson & Sam Ruby在《RESTful Web Services》一書中提出了一種名為“面向資源的架構(gòu)(ROA: Resource Oriented Architecture)”。該書中介紹了一些采用ROA架構(gòu)的Web服務(wù)應(yīng)該具備的基本特征,它們可以指導(dǎo)我們?nèi)绻麡?gòu)架具體的RESTful Web API。
二、采用URI標(biāo)識(shí)資源
SOAP Web API采用RPC風(fēng)格,它采用面向功能的架構(gòu),所以我們?cè)谠O(shè)計(jì)SOAP Web API的時(shí)候首相考慮的是應(yīng)高提供怎樣的功能(或者操作)。RESTful Web API采用面向資源的架構(gòu),所以在設(shè)計(jì)之初首先需要考慮的是有哪些資源可供操作。
資源是一個(gè)很寬泛的概念,任何寄宿于Web可供操作的“事物”均可視為資源。資源可以體現(xiàn)為經(jīng)過持久化處理保存到磁盤上的某個(gè)文件或者數(shù)據(jù)庫(kù)中某個(gè)表的某條記錄,也可以是Web應(yīng)用接受到請(qǐng)求后采用某種算法計(jì)算得出的結(jié)果。資源可以體現(xiàn)為一個(gè)具體的物理對(duì)象,它也可以是一個(gè)抽象的流程。
一個(gè)資源必須具有一個(gè)或者多個(gè)標(biāo)識(shí),既然我們?cè)O(shè)計(jì)的Web API,那么很自然地應(yīng)該采用URI來作為資源的標(biāo)識(shí)。作為資源標(biāo)識(shí)的URI最好具有“可讀性”,因?yàn)榫哂锌勺x性的URI更容易被使用,使用者一看就知道被標(biāo)識(shí)的是何種資源,比如如下一些URI就具有很好的可讀性。
- http://www.artech.com/employees/c001(編號(hào)C001的員工)
- http://www.artech.com/sales/2013/12/31(2013年12月31日的銷售額)
- http://www.artech.com/orders/2013/q4(2013年第4季度簽訂的訂單)
除了必要的標(biāo)志性和可選的可讀性之外,標(biāo)識(shí)資源的URI應(yīng)該具有“可尋址性(Addressability)”。也就是說,URI不僅僅指明了被標(biāo)識(shí)資源所在的位置,而且通過這個(gè)URI可以直接獲取目標(biāo)資源。通過前面的介紹 我們知道URI具有URL和URN兩種主要的表現(xiàn)形式,只要前者具有可尋址性,所以我們最好采用一個(gè)URL作為資源的標(biāo)識(shí)。
URI除了可以標(biāo)識(shí)某個(gè)獨(dú)立的資源外(比如“http://www.artech.com/employees/c001”),還可以標(biāo)識(shí)一組資源的集合或者資源的容器(比如“http://www.artech.com/orders/2013/q4”)。當(dāng)然,一組同類資源的集合或者存放一組同類資源的容器本身也可以視為另一種類型的復(fù)合型(Composite)資源,所以“URI總是標(biāo)識(shí)某個(gè)資源”這種說法是沒有問題的。
三、使用“鏈接”關(guān)聯(lián)相關(guān)的資源
在絕大多數(shù)情況下,資源并不會(huì)孤立地存在,必然與其它資源具有某種關(guān)聯(lián)。既然我們推薦資源采用具有可尋址性的URL來標(biāo)識(shí),那么我們就可以利用它來將相關(guān)的資源關(guān)聯(lián)起來。比如我們采用XML來表示一部電影的信息,那么我們采用如下的形式利用URL將相關(guān)的資源(導(dǎo)演、領(lǐng)銜主演、主演、編劇以及海報(bào))關(guān)聯(lián)在一起。實(shí)際上這可以視為一份超文本/超媒體文檔。當(dāng)用戶得到這樣一份文檔的時(shí)候,可以利用自身的內(nèi)容獲得某部影片基本的信息,還可以利用相關(guān)的“鏈接”得到其它相關(guān)內(nèi)容的詳細(xì)信息。
1: <movie> 2: <name>魔鬼代言人</name> 3: <genre>劇情|懸疑|驚悚</genre> 4: <directors> 5: <add ref="http://www.artech.com/directors/taylor-hackford">泰勒.海克福德</add> 6: </directors> 7: <starring> 8: <add ref = "http://www.artech.com/actors/al-pacino">阿爾.帕西諾</add> 9: <add ref = "http://www.artech.com/actors/keanu-reeves ">基諾.李維斯</add> 10: </starring> 11: <supportingActors> 12: <add ref = "http://www.artech.com/actors/charlize-theron ">查理茲.塞隆</add> 13: <add ref = "http://www.artech.com/actors/jeffrey-jones ">杰弗瑞.瓊斯</add> 14: <add ref = "http://www.artech.com/actors/connie-nielsen">康尼.尼爾森</add> 15: </supportingActors> 16: <scriptWriters> 17: <add ref = "http://www.artech.com/scriptwriters/jonathan-lemkin">喬納森?萊姆金</add> 19: <add ref = "http://www.artech.com/scriptwriters/tony-gilroy">托尼?吉爾羅伊 </add> 20: </scriptWriters> 21: <language>英語</language> 22: <poster ref = "http://www.artech.com/images/the-devil-s-advocate"/> 23: <story>...</story> 24: </movie>Fielding在他的論文中將REST定位為“分布式超媒體應(yīng)用”的架構(gòu)風(fēng)格,而超媒體的核心就是利用“鏈接”相關(guān)的信息結(jié)成一個(gè)非線性的網(wǎng),所以從一點(diǎn)也可以看出REST和“使用鏈接關(guān)聯(lián)相關(guān)的資源”這個(gè)特性使吻合的。
四、使用統(tǒng)一的接口
由于REST是面向資源的,所以一個(gè)Web API旨在實(shí)現(xiàn)針對(duì)單一資源的操作。我們?cè)谇懊嬉呀?jīng)說個(gè),針對(duì)資源的基本操作唯CRUD而已,這是使我們可以為Web API定義標(biāo)準(zhǔn)接口成可能。所謂的標(biāo)準(zhǔn)接口就是針對(duì)不同資源的Web API定義一致性的操作來操作它們,其接口可以采用類似于下面的模式。
1: public class ResourceService 2: { 3: public IEnumerable<Resource>[] Get(); 4: public void Create(Resource resource); 5: public void Update(Resource resource); 6: public void Delete(string id); 7: }能否采用統(tǒng)一接口是RESTful Web API和采用RPC風(fēng)格的SOAP Web服務(wù)又一區(qū)別。如果采用RPC風(fēng)格的話,我們?cè)谠O(shè)計(jì)Web API的時(shí)候首先考慮的是具體哪些功能需要被提供,所以這樣的Web API是一組相關(guān)功能的集合而已。
以一個(gè)具體的場(chǎng)景為例。現(xiàn)在我們需要設(shè)計(jì)一個(gè)Web API來管理用于授權(quán)的角色,它只需要提供針對(duì)角色本身的CRUD的功能以及建立/解除與用戶名之間的映射關(guān)系。如果我們將其定義成針對(duì)SOAP的Web服務(wù),其服務(wù)接口具有類似于如下的結(jié)構(gòu)。
1: public class RoleService 2: { 3: public IEnumerable<string> GetAllRoles(); 4: public void CreateRole(string roleName); 5: public void DeleteRole(string roleName); 6:? 7: public void AddRolesInUser(string userName, string[] roleNames); 8: public void RemoveRolesFromUser(string userName, string[] roleNames); 9: }如下我們需要將其定義成一個(gè)純粹的RESTful的Web API,只有前面三個(gè)方法在針對(duì)角色的CRUD操作范疇之內(nèi),但是后面兩個(gè)方法卻可以視為針對(duì)“角色委派(Role Assignment)”對(duì)象的添加和刪除操作。所以這里實(shí)際上涉及到了兩種資源,即角色和角色委派。為了使Web API具有統(tǒng)一的接口,我們需要定義如下兩個(gè)Web API。
1: public class RolesService 2: { 3: public IEnumerable<string> Get(); 4: public void Create(string roleName); 5: public void Delete(string roleName); 6: } 7:? 8: public class RoleAssignmentsService 9: { 10: public void Create(RoleAssignment roleName); 11: public void Delete(RoleAssignment roleName); 12: }?
五、使用標(biāo)準(zhǔn)的HTTP方法
由于RESTful Web API采用了同一的接口,所以其成員體現(xiàn)為針對(duì)同一資源的操作。對(duì)于Web來說,針對(duì)資源的操作通過HTTP方法來體現(xiàn)。我們應(yīng)該將兩者統(tǒng)一起來,是Web API分別針對(duì)CRUD的操作只能接受具有對(duì)應(yīng)HTTP方法的請(qǐng)求。
我們甚至可以直接使用HTTP方法名作為Web API接口的方法名稱,那么這樣的Web API接口就具有類似于如下的定義。對(duì)于ASP.NET Web API來說,由于它提供了Action方法名稱和HTTP方法的自動(dòng)映射,所以如果我們采用這樣的命名規(guī)則,就無需再為具體的Action方法設(shè)定針對(duì)HTTP方法的約束了。
1: public class ResourceService 2: { 3: public IEnumerable<Resource>[] Get(); 4: public void Post(Resource resource); 5: public void Put(Resource resource); 6: public void Patch (Resource resource); 7: public void Delete(string id); 8:? 9: public void Head(string id); 10: public void Options(); 11: }上面代碼片斷提供的7個(gè)方法涉及到了7個(gè)常用的HTTP方法,接下來我們針對(duì)資源操作的語義對(duì)它們作一個(gè)簡(jiǎn)單的介紹。首先GET、HEAD和OPTIONS這三個(gè)HTTP方法旨在發(fā)送請(qǐng)求以或者所需的信息。對(duì)于GET,相應(yīng)所有人對(duì)它已經(jīng)非常熟悉了,它用于獲取所需的資源,服務(wù)器一般講對(duì)應(yīng)的資源置于響應(yīng)的主體部分返回給客戶端。
HEAD和OPTIONS相對(duì)少見。從資源操作的語義來講,一個(gè)針對(duì)某個(gè)目標(biāo)資源發(fā)送的HEAD請(qǐng)求一般不是為了獲取目標(biāo)資源本身的內(nèi)容,而是得到描述目標(biāo)資源的元數(shù)據(jù)信息。服務(wù)器一般講對(duì)應(yīng)資源的元數(shù)據(jù)置于響應(yīng)的報(bào)頭集合返回給客戶端,這樣的響應(yīng)一般不具有主體部分。OPTIONS請(qǐng)求旨在發(fā)送一種“探測(cè)”請(qǐng)求以確定針對(duì)某個(gè)目標(biāo)地址的請(qǐng)求必須具有怎樣的約束(比如應(yīng)該采用怎樣的HTTP方法以及自定義的請(qǐng)求報(bào)頭),然后根據(jù)其約束發(fā)送真正的請(qǐng)求。比如針對(duì)“跨域資源”的預(yù)檢(Preflight)請(qǐng)求采用的HTTP方法就是OPTIONS。
至于其它4中HTTP方法(POST、PUT、PATCH和DELETE),它們旨在針對(duì)目標(biāo)資源作添加、修改和刪除操作。對(duì)于DELETE,它的語義很明確,就是刪除一個(gè)已經(jīng)存在的資源。我們著重推薦其它三個(gè)旨在完成資源的添加和修改的HTTP方法作一個(gè)簡(jiǎn)單的介紹。
通過發(fā)送POST和PUT請(qǐng)求均可以添加一個(gè)新的資源,但是兩者的不同之處在于:對(duì)于前者,請(qǐng)求著一般不能確定標(biāo)識(shí)添加資源最終采用的URI,即服務(wù)端最終為成功添加的資源指定URI;對(duì)于后者,最終標(biāo)識(shí)添加資源的URI是可以由請(qǐng)求者控制的。也正是因?yàn)檫@個(gè)原因,如果發(fā)送PUT請(qǐng)求,我們一般直接將標(biāo)識(shí)添加資源的URI作為請(qǐng)求的URI;對(duì)于POST請(qǐng)求來說,其URI一般是標(biāo)識(shí)添加資源存放容器的URI。
比如我們分別發(fā)送PUT和POST請(qǐng)求以添加一個(gè)員工,標(biāo)識(shí)員工的URI由其員工ID來決定。如果員工ID由客戶端來指定,我們可以發(fā)送PUT請(qǐng)求;如果員工ID由服務(wù)端生成,我們一般發(fā)送POST請(qǐng)求。具體的請(qǐng)求與下面提供的代碼片斷類似,可以看出它們的URI也是不一樣的。
1: PUT http://www.artech.com/employees/300357 HTTP/1.1 2: ... 3:? 4: <employee> 5: <id>300357</id> 6: <name>張三</name> 7: <gender>男<gender> 8: <birthdate>1981-08-24</birthdate> 9: <department>3041</department> 10: </employee>?
1: POST http://www.artech.com/employees HTTP/1.1 2: ... 3:? 4: <employee> 5: <name>張三</name> 6: <gender>男<gender> 7: <birthdate>1981-08-24</birthdate> 8: <department>3041</department> 9: </employee>POST和PUT請(qǐng)求一般將所加資源的內(nèi)容置于請(qǐng)求的主體。但是對(duì)于PUT請(qǐng)求來說,如果添加資源的內(nèi)容完全可以由其URI來提供,這樣的請(qǐng)求可以不需要主體。比如我們通過請(qǐng)求添加一個(gè)用于控制權(quán)限的角色,標(biāo)識(shí)添加角色的URI由其角色名稱來決定,并且不需要指定除角色名稱的其它信息,那么我們只要發(fā)送如下一個(gè)不含主體的PUT請(qǐng)求即可。
1: PUT http://www.artech.com/roles/admin HTTP/1.1 2:? 3: ...除了進(jìn)行資源的添加,PUT請(qǐng)求還能用于資源的修改。由于請(qǐng)求包含提交資源的標(biāo)識(shí)(可以放在URI中,也可以置于保存在主體部分的資源內(nèi)容中),所以服務(wù)端能夠定位到對(duì)應(yīng)的資源予以修改。對(duì)于POST和PUT,也存在一種一刀切的說法:POST用于添加,PUT用于修改。我個(gè)人比較認(rèn)可的是:如果PUT提供的資源不存在,則做添加操作,否則做修改。
對(duì)于發(fā)送PUT請(qǐng)求以修改某個(gè)存在的資源,服務(wù)器一般會(huì)將提供資源將原有資源整體“覆蓋”掉。如果需要進(jìn)行“局部”修改,我們推薦請(qǐng)求采用PATCH方法,因?yàn)閺恼Z義上講“Patch”就是打補(bǔ)丁的意思。
安全性與冪等性
關(guān)于HTTP請(qǐng)求采用的這些個(gè)方法,具有兩個(gè)基本的特性,即“安全性”和“冪等性”。對(duì)于上述7種HTTP方法,GET、HEAD和OPTIONS均被認(rèn)為是安全的方法,因?yàn)樗鼈冎荚趯?shí)現(xiàn)對(duì)數(shù)據(jù)的獲取,并不具有“邊界效應(yīng)(Side Effect[1])”。至于其它4個(gè)HTTP方法,由于它們會(huì)導(dǎo)致服務(wù)端資源的變化,所以被認(rèn)為是不安全的方法。
冪等性(Idempotent)是一個(gè)數(shù)學(xué)上的概念,在這里表示發(fā)送一次和多次請(qǐng)求引起的邊界效應(yīng)是一致的。在網(wǎng)速不夠快的情況下,客戶端發(fā)送一個(gè)請(qǐng)求后不能立即得到響應(yīng),由于不能確定是否請(qǐng)求是否被成功提交,所以它有可能會(huì)再次發(fā)送另一個(gè)相同的請(qǐng)求,冪等性決定了第二個(gè)請(qǐng)求是否有效。
上述3種安全的HTTP方法(GET、HEAD和OPTIONS)均是冪等方法。由于DELETE和PATCH請(qǐng)求操作的是現(xiàn)有的某個(gè)資源,所以它們是冪等方法。對(duì)于PUT請(qǐng)求,只有在對(duì)應(yīng)資源不存在的情況下服務(wù)器才會(huì)進(jìn)行添加操作,否則只作修改操作,所以它也是冪等方法。至于最后一種POST,由于它總是進(jìn)行添加操作,如果服務(wù)器接收到兩次相同的POST操作,將導(dǎo)致兩個(gè)相同的資源被創(chuàng)建,所以這是一個(gè)非冪等的方法。
當(dāng)我們?cè)谠O(shè)計(jì)Web API的時(shí)候,應(yīng)該盡量根據(jù)請(qǐng)求HTTP方法的冪等型來決定處理的邏輯。由于PUT是一個(gè)冪等方法,所以攜帶相同資源的PUT請(qǐng)求不應(yīng)該引起資源的狀態(tài)變化,如果我們?cè)谫Y源上附加一個(gè)自增長(zhǎng)的計(jì)數(shù)器表示被修改的次數(shù),這實(shí)際上就破壞了冪等型。
不過就我個(gè)人的觀點(diǎn)來說,在有的場(chǎng)合下針對(duì)冪等型要求可以不需要那么嚴(yán)格。舉個(gè)例子,我對(duì)于我們開發(fā)的發(fā)部分應(yīng)用來說,數(shù)據(jù)表基本上都有一個(gè)名為L(zhǎng)astUpdatedTime的字段表示記錄最后一次被修改的時(shí)間,因?yàn)檫@是為了數(shù)據(jù)安全審核(Auditing)的需要。在這種情況下,如果接收到一個(gè)基于數(shù)據(jù)修改的PUT請(qǐng)求,我們總是會(huì)用提交數(shù)據(jù)去覆蓋現(xiàn)有的數(shù)據(jù),并將當(dāng)前服務(wù)端時(shí)間(客戶端時(shí)間不可靠)作為字段LastUpdatedTime的值,這實(shí)際上也破壞了冪等性。
可能有人說我們可以在真正修改數(shù)據(jù)之前檢查提交的數(shù)據(jù)是否與現(xiàn)有數(shù)據(jù)一致,但是在涉及多個(gè)表鏈接的時(shí)候這個(gè)“預(yù)檢”操作會(huì)帶來性能損失,而且針對(duì)每個(gè)字段的逐一比較也是一個(gè)很繁瑣的事情,所以我們一般不作這樣的預(yù)檢操作。
六、支持多種資源表示方式
資源和資源的表示(Representaion)是兩個(gè)不同的概念,資源本身是一個(gè)抽象的概念,是看不見摸不著的,而看得見摸得著的是資源的表現(xiàn)。比如一個(gè)表示一個(gè)財(cái)年銷售情況的資源,它既可以表示為一個(gè)列表、一個(gè)表格或者是一個(gè)圖表。如果采用圖表,又可以使用柱狀圖、K線圖和餅圖等,這一切都是針對(duì)同一個(gè)資源的不同表示。
我們說“調(diào)用Web API獲取資源”,這句話其實(shí)是不正確的,因?yàn)槲覀儷@取的不是資源本身,僅僅是資源的某一種表示而已。對(duì)于Web來說,目前具有兩種主流的數(shù)據(jù)結(jié)構(gòu),XML和JSON,它們也是資源的兩種主要的呈現(xiàn)方式。在多語言環(huán)境下,還應(yīng)該考慮描述資源采用的語言。
我們?cè)谠O(shè)計(jì)Web API的時(shí)候,應(yīng)該支持不同的資源表示,我們不能假定請(qǐng)求提供的資源一定表示成XML,也不能總是以JSON格式返回獲取的資源,正確的做法是:根據(jù)請(qǐng)求攜帶的信息識(shí)別提交和希望返回的資源表示。對(duì)于請(qǐng)求提交的資源,我們一般利用請(qǐng)求的Content-Type報(bào)頭攜帶的媒體類型來判斷其采用的表示類型。對(duì)于響應(yīng)資源表示類型的識(shí)別,可以采用如下兩種方式。
- 讓請(qǐng)求URI包含資源表示類型,這種方式使用的最多的是針對(duì)多語言的資源,我們一般講表示語言(也可以包含地區(qū))的代碼作為URI的一部分,比如“http://www.artech.com/en/orders/2013”表示將2013年的訂單以英文的形式返回。
- 采用“內(nèi)容協(xié)商(Content Negotiation)”根據(jù)請(qǐng)求相關(guān)報(bào)頭來判斷它所希望的資源表示類型,比如“Accept”和“Accept-language”報(bào)頭可以體現(xiàn)請(qǐng)求可以接受的響應(yīng)媒體類型和語言。
對(duì)于上述兩種資源表示識(shí)別機(jī)制,我們很多人會(huì)喜歡后者,因?yàn)榈谝环N不夠“智能”。實(shí)際上前者具有一個(gè)后者不具有的特性:“瀏覽器兼容型”[2]。對(duì)于Web API開發(fā)來說,瀏覽器應(yīng)該成為一種最為常用的測(cè)試工具。在不借助任何插件的情況下,我們利用瀏覽器訪問我們?cè)诘刂窓谥休斎氲腢RI時(shí)對(duì)生成的請(qǐng)求內(nèi)容不能作任何干預(yù)的,如果與資源表示相關(guān)的信息(比如語言、媒體類型)被直接包含到請(qǐng)求的URI中,那么所有的情況都可以利用瀏覽器直接測(cè)試。
有人從另一方面對(duì)“URI攜帶資源表示類型”作了這樣的質(zhì)疑:由于URI是資源的標(biāo)識(shí),那么這導(dǎo)致了相同的資源具有多個(gè)標(biāo)識(shí)。其實(shí)這是沒有問題的,URI是資源的唯一標(biāo)識(shí),但不是其“唯一的唯一標(biāo)識(shí)“,相同的資源可以具有多個(gè)標(biāo)識(shí)。
七、無狀態(tài)性
RESTful只要維護(hù)資源的狀態(tài),而不需要維護(hù)客戶端的狀態(tài)。對(duì)于它來說,每次請(qǐng)求都是全新的,它只需要針對(duì)本次請(qǐng)求作相應(yīng)的操作,不需要將本次請(qǐng)求的相關(guān)信息記錄下來以便用于后續(xù)來自相同客戶端請(qǐng)求的處理。
對(duì)于上面我們介紹的RESTful的這些個(gè)特性,它們都是要求我們?yōu)榱藵M足這些特征做點(diǎn)什么,唯有這個(gè)無狀態(tài)卻是要求我們不要做什么,因?yàn)镠TTP本身就是無狀態(tài)的。舉個(gè)例子,一個(gè)網(wǎng)頁(yè)通過調(diào)用Web API分頁(yè)獲取符合查詢條件的記錄。一般情況下,頁(yè)面導(dǎo)航均具有“上一頁(yè)”和“下一頁(yè)”鏈接用于呈現(xiàn)當(dāng)前頁(yè)的前一頁(yè)和后一頁(yè)的記錄。那么現(xiàn)在有兩種實(shí)現(xiàn)方式返回上下頁(yè)的記錄。
- Web API不僅僅會(huì)定義根據(jù)具體頁(yè)碼的數(shù)據(jù)查詢定義相關(guān)的操作,還會(huì)針對(duì)“上一頁(yè)”和“下一頁(yè)”這樣的請(qǐng)求定義單獨(dú)的操作。它自身會(huì)根據(jù)客戶端的Session ID對(duì)每次數(shù)據(jù)返回的頁(yè)面在本地進(jìn)行保存,以便能夠知道上一頁(yè)和下一頁(yè)具體是哪一頁(yè)。
- Web API只會(huì)定義根據(jù)具體頁(yè)碼的數(shù)據(jù)查詢定義相關(guān)的操作,當(dāng)前返回?cái)?shù)據(jù)的頁(yè)碼由客戶端來維護(hù)。
第一種貌似很“智能”,其實(shí)就是一種畫蛇添足的作法,因?yàn)樗茐牧薟eb API的無狀態(tài)性。設(shè)計(jì)無狀態(tài)的Web API不僅僅使Web API自身顯得簡(jiǎn)單而精煉,還因減除了針對(duì)客戶端的“親和度(Affinty)”使我們可以有效地實(shí)施負(fù)載均衡,因?yàn)橹挥羞@樣集群中的每一臺(tái)服務(wù)器對(duì)于每個(gè)客戶端才是等效的。
[1]?大部分計(jì)算機(jī)書籍都將Side Effect翻譯成“副作用”,而我們一般將“副(負(fù))作用”理解為負(fù)面的作用,其實(shí)計(jì)算機(jī)領(lǐng)域Side Effect表示的作用無所謂正負(fù),所以我們覺得還是還原其字面的含義“邊界效用”。除此之外,對(duì)于GET、HEAD和OPTIONS請(qǐng)求來說,如果服務(wù)端需要對(duì)它們作日志、緩存甚至計(jì)數(shù)操作,嚴(yán)格來說這也算是一種Side Effect,但是請(qǐng)求的發(fā)送者不對(duì)此負(fù)責(zé)。
[2]?這里的“兼容”不是指支持由瀏覽器發(fā)送的請(qǐng)求,因?yàn)橥ㄟ^執(zhí)行JavaScript腳本可以讓作為宿主的瀏覽器發(fā)送任何我們希望的請(qǐng)求,這里的兼容體現(xiàn)在盡可能地支持瀏覽器訪問我們?cè)诘刂窓谥休斎氲腢RI默認(rèn)發(fā)送的HTTP-GET請(qǐng)求。
參考資料:?
[1] 《HTTP: The Definitive Guide》, By By David Gourley, Brian Totty, Marjorie Sayer, Anshu Aggarwal, Sailu Reddy?
[2] 《RESTful Web Services》, RESTful Web Services?
[3] 《A Brief Introduction to REST》,http://www.infoq.com/articles/rest-introduction?
[4] 《TCP/IP Illustrated (Volumn 1: The Protocol)》, by W. Richard Stevens
我所理解的RESTful Web API [Web標(biāo)準(zhǔn)篇]?
我所理解的RESTful Web API [設(shè)計(jì)篇]
作者:蔣金楠?
微信公眾賬號(hào):大內(nèi)老A
微博:www.weibo.com/artech
from:?https://www.cnblogs.com/artech/p/restful-web-api-02.html
總結(jié)
以上是生活随笔為你收集整理的我所理解的RESTful Web API [设计篇]的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: web请求过程
- 下一篇: spring boot(一):入门篇