天天看點

什麼才是真正的 RESTful 架構?

2015年11月11日

更多内容請看我最近在寫的一本小書:《Microservice 最佳實踐之路》,實為項目上的踩坑手冊。

What?

Wikipedia: 表征性狀态傳輸(英文:Representational State Transfer,簡稱REST)是Roy Fielding博士于2000年在他的博士論文中提出來的一種軟體架構風格。

Roy Fielding是HTTP協定(1.0版和1.1版)的主要設計者,事實上HTTP 1.1規範正是基于REST架構風格的指導原理來設計的。需要注意的是,REST是一種設計風格而不是标準,如果一個架構符合REST原則,我們就稱它為RESTful架構。

Why?

什麼才是真正的 RESTful 架構?

在「遠古時代」前端後端是融合在一起的,比如之前的PHP,JSP,ASP等等(參考我之前做過的一場演講:Web前端開發模式演變)。近年來随着移動網際網路的飛速發展,各種類型的Client端層出不窮,就需要通過一套統一的接口分别為Web,iOS和Android乃至桌面端提供服務。另外對于廣大平台來說,比如Facebook platform,微網誌開放平台,微信公共平台等,它們不需要有顯式的前端,隻需要一套提供服務的接口,于是RESTful更是它們最好的選擇。

而要了解RESTful架構,最好的方法就是去了解Representational State Transfer這個詞組,直譯過來就是「表現層狀态轉化」,其實它省略了主語。「表現層」其實指的是「資源」的「表現層」,是以通俗來講就是:資源在網絡中以某種表現形式進行狀态轉移。分解開來:

  • Resource:資源,即資料。比如newsfeed,friends,order等;
  • Representational:某種表現形式,比如用JSON,XML,JPEG等;
  • State Transfer:狀态變化。通過HTTP動詞實作。

然後再來了解一個具體的RESTful架構——面向資源的架構(Resource-Oriented Architecture,ROA):

  • 資源是由URI來指定。所謂「上網」,就是與網際網路上一系列的「資源」互動,調用它的URI。
  • 對資源的操作包括擷取、建立、修改和删除資源,這些操作正好對應HTTP協定提供的GET、POST、PUT和DELETE方法。
  • 通過操作資源的表現形式來操作資源。具體表現形式,應該在HTTP請求的頭資訊中用Accept和Content-Type字段指定。
  • 資源的表現形式則是XML或者HTML,取決于讀者是機器還是人,是消費web服務的客戶軟體還是web浏覽器。當然也可以是任何其他的格式。

How?

應用于Web服務,符合REST設計風格的Web API稱為RESTful API。它從以下三個方面資源進行定義:

  • 直覺簡短的資源位址:URI,比如:

    http://example.com/resources/

    ;每一個URI代表一種資源;
  • 傳輸的資源:Web服務接受與傳回的網際網路媒體類型,比如:JSON,XML,YAML等。
  • 對資源的操作:Web服務在該資源上所支援的一系列請求方法(比如:POST,GET,PUT或DELETE)。

來個圖,

什麼才是真正的 RESTful 架構?

HTTP請求方法在RESTful API中的典型應用:

資源 GET PUT POST DELETE
一組資源的URI,比如

http://example.com/resources/

列出URI,以及該資源組中每個資源的詳細資訊(後者可選)。 使用給定的一組資源替換目前整組資源。 在本組資源中建立/追加一個新的資源。該操作往往傳回新資源的URL。 删除整組資源。
單個資源的URI,比如

http://example.com/resources/142

擷取指定的資源的詳細資訊,格式可以自選一個合适的網絡媒體類型(比如:XML、JSON等) 替換/建立指定的資源。并将其追加到相應的資源組中。 把指定的資源當做一個資源組,并在其下建立/追加一個新的元素,使其隸屬于目前資源。 删除指定的元素。

REST的誤解

現在看來,REST在2000年那個時代,确實是超前于時代的。Web開發者社群對于HTTP的設計意圖存在着大量的誤解,由此導緻了對于HTTP的大量低效率的誤用。這個情況持續一直到2005年Web 2.0的崛起。那個時候,DCOM、EJB、SOAP/WSDL這些DO風格的架構由于難以滿足網際網路環境對分布式應用架構設計的限制,與Web自身的架構風格REST相沖突,很難融入到Web之中。所謂的「Web Services」,其實除了将HTTP作為底層的傳輸協定外,跟(網際網路環境中的)真正的Web沒有什麼關系。

而随着Ruby on Rails這個著名的Web開發架構開始大力支援REST開發之後,一線的Web開發者才真正接觸到了REST。然而Rails所支援的REST開發将對資源的操作局限于CRUD(建立、擷取、修改、删除)的語義(即,将對資源的CRUD操作映射到 GET/POST/PUT/DELETE四個HTTP方法),這其實是收窄了REST的适用範圍。其他程式設計語言的Web開發架構(例如Java語言的 Struts、Spring MVC等等)也緊接着模仿了Rails的方式開始支援REST開發,然而這更加導緻了一線的Web開發者誤以為:REST開發就是 通過GET/POST/PUT/DELETE四個HTTP方法對資源執行CRUD操作。甚至還有很多僅僅使用了HTTP,而沒有使用SOAP的Web服 務API,都自稱是REST風格(RESTful)的API。

對于什麼才是真正的REST風格的誤解是如此之多,而将REST作為一個便于營銷的 buzzword的挂羊頭賣狗肉者也是如此之多,以至于REST的創造者Fielding終于忍無可忍了。2008年10月Fielding寫了一篇博 客,做出了一個非常明确的斷言:REST APIs must be hypertext-driven!(REST API必須是超文本驅動的!)超文本驅動這個理念變成了一個縮寫詞HATEOAS,這個縮寫詞來自于當初Fielding博士論文中的一句話: hypermedia as the engine of application state(将超媒體作為應用狀态的引擎)。其實超文本驅動(Hypertext Driven)的理念才是REST架構風格最核心的理念,也是REST風格的架構達到松耦合目标的根本原因。

REST設計進階

當談及REST成熟度時,一些人常常會引用Richardson所提出來的REST成熟度模型(Maturity Model),并視之為正确的度量方法。

什麼才是真正的 RESTful 架構?

第一級:在架構中引入資源(Resource)的概念。

大多數WS-*服務和POX都隻是使用一個URI作為一個服務端口,也隻使用一個HTTP方法傳輸資料。這種做法相當于把HTTP這個應用層協定降級為傳輸層協定用,《REST實戰》也一再強調HTTP是一種應用協定而不是傳輸協定。再好一點就是使用多個URI,然而不同的URI隻是作為不同的調用入口,與此同時隻使用同一個HTTP方法傳輸資料。最常見的錯誤就是在URI中包含動詞,比如URI 

http://example.com/getOrder?orderId=1234

,其實「資源」表示一種實體,是以應該是名詞,動詞應該放在HTTP協定中。而與此同時URI也有可能破壞HTTP GET的安全性和幕等性,比如某個用戶端在

http://example.com/updateOrder?id=1234&coffee=latte

上執行GET(而不是POST),就能建立一筆新的咖啡訂單(一個資源),按理來說GET請求不能改變服務的任何狀态。

第二級:每一個URI代表一種資源,支援HTTP動詞。

此時使用多個URI的話,需要讓不同的URI代表不同的資源(注意多個URI可能指向同一個Resource,而一個URI不能指向不同Resource。),同時使用多個HTTP方法操作這些資源,例如使用POST/GET/PUT/DELET分别進行CRUD操作。這時候HTTP頭和有效載荷都包含業務邏輯,例如HTTP方法對應CRUD操作,HTTP狀态碼對應操作結果的狀态。我們現在看到的大多數所謂RESTful API做到的也就是這個級别。《REST實戰》的譯者也談到:悟性差的人,了解到CRUD式Web服務就滿足了。而悟性好的人,可以徹底了解超文本驅動,甚至是與REST關系密切的語義網,最終達到 REST開發的最高境界。

第三級:HATEOAS,使用超媒體(hypermedia)作為應用狀态引擎。

根據Roy的嚴格規定,超媒體(hypermedia)是REST的先決條件。任何其他東西不應該自我标榜為REST。要解釋HATEOAS這個概念先要解釋什麼是超媒體:我們已經知道什麼是多媒體(multimedia),以及什麼是超文本(hypertext)。其中超文本特有的優勢是擁有超連結(hyperlink)。如果我們把超連結引入到多媒體當中去,那就得到了超媒體,是以關鍵角色還是超連結。使用超媒體作為應用引擎狀态,意思是應用引擎的狀态變更由用戶端通路不同的超媒體資源驅動。

讓我們來看個執行個體,這個響應内容可能略有不同:

GET https://api.example.com/profile

{
  "name": "Steve",
  "picture": {
    "large": "https://somecdn.com/pictures/1200x1200.png",
    "medium": "https://somecdn.com/pictures/100x100.png",
    "small": "https://somecdn.com/pictures/10x10.png"
  }
}           

由于在響應中包含了連結位址,是以使用該API的用戶端就能夠自由選擇要下載下傳怎樣的資訊。這些連結告知了用戶端有哪些選擇,并且它們的位址在哪裡。是以在這裡我們無需同時傳回三個不同版本的使用者檔案圖檔,我們所做的隻是告訴用戶端有三種可用的圖檔尺寸可以選擇,并且告訴用戶端能夠在哪裡找到這些圖檔。這樣一來,用戶端就能夠根據不同的場景,做出符合自身需要的選擇。而且,如果用戶端隻需要一種格式的圖檔,那就無需下載下傳全部三種版本的圖檔了。這樣一來可謂一箭三雕:既減少了網絡負載,又增進了用戶端的靈活性,更增進了API的可探索性。

超媒體的核心概念就是所謂的

<link>

元素,而這些互相連結的資源實際上描述了一個協定,即引導我們達成某個目标的一系列步驟,例如訂購一杯咖啡所需要的點單、付款、取咖啡等等。這就是超媒體的本質:經由資源之間的連結,我們改變整個應用的狀态,即超媒體轉換了分布式應用的狀态。需要注意的是,伺服器和消費者兩者間交換的是資源狀态的表述,而不是應用的狀态,被轉移的表述中包括了反應應用狀态的連結。

什麼才是真正的 RESTful 架構?

在這裡也推薦直接看看設計非常優秀的GitHub API,以便于更好地了解真實的RESTful API以及hypermedia的概念。

Reference

  • What Is REST? – Learn REST: A RESTful Tutorial
  • 什麼是REST與Go語言的RESTful實作
  • 了解RESTful架構 – 阮一峰
  • REST 架構該怎麼生動地了解? – 知乎
  • 深入淺出REST - InfoQ
  • 面向資源的架構 – 《RESTful Web Services中文版》
  • 《REST實戰》
  • 怎麼樣才算是 RESTful?讀 REST in Practice
  • Richardson Maturity Model – Martin Fowler
  • 實作超媒體
  • GitHub API文檔

文章轉自《http://blog.jimmylv.info/2015-11-11-what-is-really-rest/》,感謝作者分享!