互聯網開發常識譜--REST

什麼是REST?

REST全稱是Representational State Transfer(表述性狀態轉移),REST指的是一組架構約束條件和原則。

符合以下架構約束的稱爲REST架構風格:

  • 客戶-服務器(Client-Server)
    通信只能由客戶端單方面發起,表現爲請求-響應的形式。
  • 無狀態(Stateless)
    通信的會話狀態(Session State)應該全部由客戶端負責維護。
  • 緩存(Cache)
    響應內容可以在通信鏈的某處被緩存,以改善網絡效率。
  • 統一接口(Uniform Interface)
    通信鏈的組件之間通過統一的接口相互通信,以提高交互的可見性。
  • 分層系統(Layered System)
    通過限制組件的行爲(即,每個組件只能“看到”與其交互的緊鄰層),將架構分解爲若干等級的層。
  • 按需代碼(Code-On-Demand,可選)
    支持通過下載並執行一些代碼(例如Java Applet、Flash或JavaScript),對客戶端的功能進行擴展。

這6個特徵是REST架構設計優秀程度的判斷標準。其中,面向資源是REST最明顯的特徵,即,REST架構設計是以資源抽象爲核心展開的。可尋址說的是:每一個資源在Web之上都有自己的地址。連通性說的是:應該儘量避免設計孤立的資源,除了設計資源本身,還需要設計資源之間的關聯關係,並且通過超鏈接將資源關聯起來。
這裏寫圖片描述
REST的五個關鍵詞:

  • 資源(Resource)
  • 資源的表述(Representation)
  • 狀態轉移(State Transfer)
  • 統一接口(Uniform Interface)
  • 超文本驅動(Hypertext Driven)

    什麼是資源?

資源是一種看待服務器的方式,即,將服務器看作是由很多離散的資源組成。每個資源是服務器上一個可命名的抽象概念。因爲資源是一個抽象的概念,所以它不僅僅能代表服務器文件系統中的一個文件、數據庫中的一張表等等具體的東西,可以將資源設計的要多抽象有多抽象,只要想象力允許而且客戶端應用開發者能夠理解。與面向對象設計類似,資源是以名詞爲核心來組織的,首先關注的是名詞。一個資源可以由一個或多個URI來標識。URI既是資源的名稱,也是資源在Web上的地址。對某個資源感興趣的客戶端應用,可以通過資源的URI與其進行交互。

什麼是資源的表述?

資源的表述是一段對於資源在某個特定時刻的狀態的描述。可以在客戶端-服務器端之間轉移(交換)。資源的表述可以有多種格式,例如HTML/XML/JSON/純文本/圖片/視頻/音頻等等。資源的表述格式可以通過協商機制來確定。請求-響應方向的表述通常使用不同的格式。

什麼是狀態轉移?

狀態轉移(state transfer)與狀態機中的狀態遷移(state transition)的含義是不同的。狀態轉移說的是:在客戶端和服務器端之間轉移(transfer)代表資源狀態的表述。通過轉移和操作資源的表述,來間接實現操作資源的目的。

什麼是統一接口?

REST要求,必須通過統一的接口來對資源執行各種操作。對於每個資源只能執行一組有限的操作。以HTTP/1.1協議爲例,HTTP/1.1協議定義了一個操作資源的統一接口,主要包括以下內容:

  • 7個HTTP方法:GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONS
  • HTTP頭信息(可自定義)
  • HTTP響應狀態代碼(可自定義)
  • 一套標準的內容協商機制
  • 一套標準的緩存機制
  • 一套標準的客戶端身份認證機制

REST還要求,對於資源執行的操作,其操作語義必須由HTTP消息體之前的部分完全表達,不能將操作語義封裝在HTTP消息體內部。這樣做是爲了提高交互的可見性,以便於通信鏈的中間組件實現緩存、安全審計等等功能。

什麼是超文本驅動?

“超文本驅動”又名“將超媒體作爲應用狀態的引擎”(Hypermedia As The Engine Of Application State,來自Fielding博士論文中的一句話,縮寫爲HATEOAS)。將Web應用看作是一個由很多狀態(應用狀態)組成的有限狀態機。資源之間通過超鏈接相互關聯,超鏈接既代表資源之間的關係,也代表可執行的狀態遷移。在超媒體之中不僅僅包含數據,還包含了狀態遷移的語義。以超媒體作爲引擎,驅動Web應用的狀態遷移。通過超媒體暴露出服務器所提供的資源,服務器提供了哪些資源是在運行時通過解析超媒體發現的,而不是事先定義的。從面向服務的角度看,超媒體定義了服務器所提供服務的協議。客戶端應該依賴的是超媒體的狀態遷移語義,而不應該對於是否存在某個URI或URI的某種特殊構造方式作出假設。一切都有可能變化,只有超媒體的狀態遷移語義能夠長期保持穩定。

從架構風格的抽象高度來看,常見的分佈式應用架構風格有三種:

  • 分佈式對象(Distributed Objects,簡稱DO)
    架構實例有CORBA/RMI/EJB/DCOM/.NET Remoting等等
  • 遠程過程調用(Remote Procedure Call,簡稱RPC)
    架構實例有SOAP/XML-RPC/Hessian/Flash AMF/DWR等等
  • 表述性狀態轉移(Representational State Transfer,簡稱REST)
    架構實例有HTTP/WebDAV

REST與RPC的差別在於REST支持抽象的工具是資源,RPC支持抽象的工具是過程。REST風格的架構建模是以名詞爲核心的,RPC風格的架構建模是以動詞爲核心的。簡單類比一下,REST是面向對象編程,RPC則是面向過程編程。

REST典型設計誤區

最常見的一種設計錯誤,就是URI包含動詞。因爲”資源”表示一種實體,所以應該是名詞,URI不應該有動詞,動詞應該放在HTTP協議中。
舉例來說,某個URI是/posts/show/1,其中show是動詞,這個URI就設計錯了,正確的寫法應該是/posts/1,然後用GET方法表示show。
如果某些動作是HTTP動詞表示不了的,你就應該把動作做成一種資源。比如網上匯款,從賬戶1向賬戶2匯款500元,錯誤的URI是:

  POST /accounts/1/transfer/500/to/2

正確的寫法是把動詞transfer改成名詞transaction,資源不能是動詞,但是可以是一種服務:

  POST /transaction HTTP/1.1
  Host: 127.0.0.1
  from=1&to=2&amount=500.00

另一個設計誤區,就是在URI中加入版本號:

  http://www.example.com/app/1.0/foo
  http://www.example.com/app/1.1/foo
  http://www.example.com/app/2.0/foo

因爲不同的版本,可以理解成同一種資源的不同表現形式,所以應該採用同一個URI。版本號可以在HTTP請求頭信息的Accept字段中進行區分:
  Accept: vnd.example-com.foo+json; version=1.0
  Accept: vnd.example-com.foo+json; version=1.1
  Accept: vnd.example-com.foo+json; version=2.0

REST實踐

URI設計技巧

  • 使用_或-來讓URI可讀性更好
  • 使用/來表示資源的層級關係
  • 使用?用來過濾資源
  • ,或;可以用來表示同級資源的關係

有時候我們需要表示同級資源的關係時,可以使用,或;來進行分割。例如哪天github可以比較某個文件在隨意兩次提交記錄之間的差異, 或許可以使用/git/git /block-sha1/sha1.h/compare/e3af72cdafab5993d18fae056f87e1d675913d08; bd63e61bdf38e872d5215c07b264dcc16e4febca作爲URI。 不過,現在github是使用…來做這個事情的,例如/git/git/compare/master…next。

實踐中常見的問題

  • POST和PUT用於創建資源時有什麼區別?
    POST和PUT在創建資源的區別在於,所創建的資源的名稱(URI)是否由客戶端決定。 例如爲我的博文增加一個java的分類,生成的路徑就是分類名/categories/java,那麼就可以採用PUT方法。 不過很多人直接把POST、GET、PUT、DELETE直接對應上CRUD,例如在一個典型的rails實現的RESTFul應用中就是這麼做的。

  • 如果GET請求增加計數器,這是否違反安全性?
    安全性不代表請求不產生副作用,例如像很多API開發平臺,都對請求流量做限制。像github,就會限制沒有認證的請求每小時只能請求60次。 但客戶端不是爲了追求副作用而發出這些GET或HEAD請求的,產生副作用是服務端“自作主張”的。 另外,服務端在設計時,也不應該讓副作用太大,因爲客戶端認爲這些請求是不會產生副作用的。

  • 直接忽視緩存可取嗎?
    即使你按各個動詞的原本意圖來使用它們,你仍可以輕易禁止緩存機制。 最簡單的做法就是在你的HTTP響應裏增加這樣一個報頭: Cache-control: no-cache。 但是,同時你也對失去了高效的緩存與再驗證的支持(使用Etag等機制)。 對於客戶端來說,在爲一個REST式服務實現程序客戶端時,也應該充分利用現有的緩存機制,以免每次都重新獲取表示。

  • 響應代碼的處理有必要嗎?
    HTTP的響應代碼可用於應付不同場合,正確使用這些狀態代碼意味着客戶端與服務器可以在一個具備較豐富語義的層次上進行溝通。 例如,201(“Created”)響應代碼表明已經創建了一個新的資源,其URI在Location響應報頭裏。 假如你不利用HTTP狀態代碼豐富的應用語義,那麼你將錯失提高重用性、增強互操作性和提升鬆耦合性的機會。 如果這些所謂的RESTFul應用必須通過響應實體才能給出錯誤信息,那麼SOAP就是這樣的了,它就能夠滿足了。

本文彙編自一些博客和文章:
理解RESTFul架構
http://mccxj.github.io/blog/20130530_introduce-to-rest.html
理解本真的REST架構風格
http://www.infoq.com/cn/articles/understanding-restful-style
理解RESTful架構
http://www.ruanyifeng.com/blog/2011/09/restful.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章