本文探討以下幾個問題:web
- 什麼是REST
- REST包含哪些約束
- 什麼是RESTful
- 純RESTful API的難點在哪裏
若是你去搜索「什麼是REST」的話,大部分狀況下,你看到的基本都是RESTful!api
這類內容主要說的是:瀏覽器
- 資源URL應該怎麼寫
- 要用GET來獲取資源
- 要用POST來新建資源
- 要用PUT來更新資源
- 要用DELETE來刪除資源
而實際上REST並非這些,或者說並不徹底是這些!緩存
什麼是REST
REST全稱Representational State Transfer,出自Roy Thomas Fielding博士的博士論文《Architectural Styles and
the Design of Network-based Software Architectures》第五章(Fielding博士的這篇論文會在後面單獨討論),通常翻譯爲「表述性狀態轉移」。服務器
在論文的第6章第一節提到了爲何會取REST這麼一個名字:restful
The name 「Representational State Transfer」 is intended to evoke an image of how a well-designed Web application behaves: a network of web pages (a virtual state-machine), where the user progresses through the application by selecting links (state transitions), resulting in the next page (representing the next state of the application) being transferred to the user and rendered for their use.
「表述性狀態轉移」這個名稱是爲了喚起人們對於一個良好設計的 Web 應用如何運轉的印象:一個由網頁組成的網絡(一個虛擬狀態機),用戶經過選擇連接在應用中前進(狀態遷移),致使下一個頁面(應用的下一個狀態的表述)被轉移給用戶,而且呈現給他們,以便他們來使用。網絡
在「什麼是架構模式和架構風格」一文中,提到過:架構
因此,REST是一組架構約束!app
REST約束
REST是一個複合架構風格,即它包含了其它的架構風格!負載均衡
REST約束包括:CS,無狀態,分層,緩存,統一接口以及按需代碼。其中「統一接口」是REST與其它架構風格的主要區別所在!「統一接口」包括了四個子約束:資源的識別,經過表述操做資源,自描述的消息,超媒體做爲應用狀態引擎!
《Architectural Styles and
the Design of Network-based Software Architectures》第五章給出了REST的完整推導過程,這裏簡單列出!
從一個沒有約束的架構(NullStyle)開始,不斷的添加約束,使此架構進化爲須要的架構
Null Style:組件之間沒有顯著邊界的系統,一個沒有約束的架構
Client-Server:
- 約束:分離關注點(客戶端接口和服務端數據存儲)
- 優點:客戶端和服務端能夠獨立的進化。客戶端能夠與多個服務端通訊,服務端能夠方便的伸縮
- 劣勢:下降了性能
Stateless:
- 約束:通訊必須在本質上是無狀態的。從客戶到服務器的每一個請求都必須包含理解該請求所必需的全部信息,不能利用任何存儲在服務器上的上下文,會話狀態所以要所有保存在客戶端
- 優點:改善可見性,監視系統沒必要爲了肯定一個請求的所有性質而去查看該請求以外的多個請求。改善可靠性,減輕了從局部故障中恢復的任務量。改善可伸縮性,沒必要在多個請求之間保存狀態,從而容許服務器組件迅速釋放資源,並進一步簡化其實現,由於服務器沒必要跨多個請求管理資源的使用。
- 劣勢:下降網絡性能,因爲不能將狀態數據保存在服務器上的共享上下文中,所以增長了在一系列請求中發送的重複數據(每次交互的開銷)。將應用狀態放在客戶端,下降了服務器對於一致的應用行爲的控制。
Cache:
- 約束:一個請求的響應中的數據被隱式地或顯式地標記爲可緩存的或不可緩存的
- 優點:改善網絡性能。若是響應是可緩存的,那麼客戶端緩存就能夠爲之後的相同請求重用這個響應的數據。可部分或所有消除一些交互,從而經過減小一系列交互的平均延遲時間,來提升效率、可伸縮性和用戶可覺察的性能。
- 劣勢:可能下降可靠性,緩存中陳舊的數據與將請求直接發送到服務器獲得的數據可能差異很大。
Uniform Interface(REST核心特徵):
- 約束:組件之間要有一個統一的接口,包括四個子約束:
- 資源的識別(identification of resources):每一個資源都有各自的標識符。客戶端在請求時須要指定該標識符。客戶端所獲取的是資源的表述,如HTML,XML 或 JSON 格式等。
- 經過表述操做資源(manipulation of resources through representations):客戶端操做的是資源的表述,而不是資源自己。
- 自描述的消息(self-descriptive messages):每條消息都包含足夠的信息來描述如何處理該消息。
- 超媒體做爲應用狀態引擎(HATEOAS)(hypermedia as the engine of application state):客戶端經過服務器提供的超媒體內容來了解如何操做表述,經過將對錶述的操做提交到服務端,服務端來操做資源,繼而改變服務端的狀態。
- 優點:簡化總體架構,改善可見性,促進獨立的可進化性。
- 劣勢:下降了效率。信息都使用標準化的形式來轉移,而不能使用特定於應用的需求的形式。
** Layered System**:
- 約束:經過限制組件的行爲,將架構分解爲若干等級的層。
- 優點:經過將組件對系統的知識限制在單一層內,爲整個系統的複雜性設置了邊界,而且提升了底層獨立性。使用層來封裝遺留的服務,使新的服務免受遺留客戶端的影響;經過將不經常使用的功能轉移到一個共享的中間組件中,從而簡化組件的實現。中間組件還可以經過支持跨多個網絡和處理器的負載均衡,來改善系統的可伸縮性。
- 劣勢:增長了數據處理的開銷和延遲,所以下降了用戶可覺察的性能。能夠經過在中間層使用共享緩存來彌補這一缺點。
Code-On-Demand(可選):
- 約束:一個客戶端組件知道如何訪問一組資源,但不知道如何處理它們。它向一個遠程服務器發送對於如何處理資源的代碼的請求,接收這些代碼,而後在本地執行這些代碼
- 優點:可以爲一個已部署的客戶端添加功能,改善了可擴展性和可配置性;當代碼可以使它的動做適應於客戶端的環境,並在本地與用戶交互而不是經過遠程交互時,可以獲得更好的用戶可覺察性能和效率。因爲服務器將工做交給了客戶端(不然將消耗服務器的資源),從而改善了服務器的可伸縮性。
- 劣勢:因爲須要管理求值環境,下降了簡單性,在一些狀況下能夠經過簡化靜態的客戶端功能獲得補償。最大的限制是因爲服務器發送代碼而不是簡單的數據,所以缺少可見性。若是客戶端沒法信任服務器,缺少可見性會致使明顯的部署問題。
總結:
可能看完了推導,你仍是不知道REST是什麼!下面我經過一個列子來解釋什麼是「REST」!
舉個例子
咱們先看看Fielding博士爲何要設計REST?Fielding博士在論文裏提到了,他設計REST是爲了指導現代Web架構的設計與開發!基於REST,Fielding博士設計了HTTP1.1!也就是說,HTTP1.1是符合REST的!因此要搞懂REST,只要理解HTTP1.1就能夠了!
若是你作過Web應用,那麼CS,分層,無狀態,緩存應該都很好理解,這裏就不贅述了!按需代碼就是相似Flash,Applet這類Web端應用,用以擴展Web功能的!
這裏只說一下「統一接口」這個約束!
咱們就以一個簡單的HTTP請求來解釋REST!
好比你輸入www.abc.com時:
- 你經過標示符來定位到www.abc.com網站的首頁,abc站點將首頁資源組裝成Response信息返回到你的瀏覽器(資源的識別)
- 返回的內容頭裏(HTTP header),告訴了瀏覽器該如何處理返回的信息(自描述的消息)
- 返回的信息體(HTTP body),通常是HTML格式,它是你訪問的首頁資源的表述,裏面包含了你能對這個表述進行的操做,好比能訪問哪些連接,能提交哪些數據(超媒體做爲應用狀態引擎)
- 你點擊連接後,這是對錶述的操做,你根本沒有接觸到真實的資源(經過表述操做資源)
- 服務端接收到你的請求後,將對應連接的資源組裝成Response信息返回(此時應用的狀態就改變了)。
- 瀏覽器接到返回後,將頁面渲染出來,你能夠進行下一步的操做。
什麼是RESTful
上面解釋了什麼是「REST」!如今來解釋一下什麼是RESTful!
前面說了,REST是一組架構約束!那麼,若是一個應用知足了REST約束,那麼咱們就能夠稱這個應用是RESTful的!
雖然,不少系統自稱是RESTful的,可是,實際上,絕大部分系統都不是RESTful的,或者不是徹底RESTful的!Fielding博士對這個問題,發表了一篇博文,明確什麼系統才能稱爲是符合我REST的!文中明確說明,系統必須知足HATEOAS約束才能稱爲是符合REST的!而HATEOAS很難實現!由於有人的參與!
爲了緩解這個尷尬,Richardson 提出了「REST成熟度模型」。該模型把 REST 服務按照成熟度劃分紅 4 個層次:
- 第一個層次(Level 0)的 Web 服務只是使用 HTTP 做爲傳輸方式,實際上只是遠程方法調用(RPC)的一種具體形式。SOAP 和 XML-RPC 都屬於此類。
- 第二個層次(Level 1)的 Web 服務引入了資源的概念。每一個資源有對應的標識符和表述。
- 第三個層次(Level 2)的 Web 服務使用不一樣的 HTTP 方法來進行不一樣的操做,而且使用 HTTP 狀態碼來表示不一樣的結果。如 HTTP GET 方法來獲取資源,HTTP DELETE 方法來刪除資源。
- 第四個層次(Level 3)的 Web 服務使用 HATEOAS。在資源的表述中包含了連接信息。客戶端能夠根據連接來發現能夠執行的動做。
從上述 REST 成熟度模型中能夠看到,使用 HATEOAS 的 REST 服務是成熟度最高的,也是推薦的作法。
- 對於不使用 HATEOAS 的 REST 服務,客戶端和服務器的實現之間是緊密耦合的。客戶端須要根據服務器提供的相關文檔來了解所暴露的資源和對應的操做。當服務器發生了變化時,如修改了資源的 URI,客戶端也須要進行相應的修改。
- 而使用 HATEOAS 的 REST 服務中,客戶端能夠經過服務器提供的資源的表達來智能地發現能夠執行的操做。當服務器發生了變化時,客戶端並不須要作出修改,由於資源的 URI 和其餘信息都是動態發現的。
如今大部分的自稱是RESTful的系統,通常只能達到第三個層次!
HATEOAS的難點
HATEOAS爲何難以實現?是由於客戶端沒法決策!HTTP能實現RESTful,是由於瀏覽器只是將表述以及對資源的操做選項展現了出來,至於具體該如何操做,是由使用瀏覽器的人來決定的!也就是說,雖然服務端告訴了客戶端操做的可選項,可是客戶端沒辦法知道該選擇什麼!
網頁瀏覽是有人的參與的,可是RESTful API是沒有人蔘與的,這就致使RESTful API的客戶端難以作出決定,該作什麼!
可行的解決辦法是:
- 語義分析:客戶端具備語義分析能力,可以自動的分析出後面須要執行哪一個操做,目前這個很難實現
- 客戶端領域設計:客戶端引入領域設計,在一個領域內,客戶端和服務端達成共識,在特定領域內,目前有哪些操做。不過這個仍是作不到只修改服務端就能夠實現系統的進化。服務端進化後,客戶端須要作對應的調整才能夠完成整個系統的進化。
參考資料