關於REST和restful


什么是REST

Representational State Transfer

越來越多的人開始意識到,網站即軟件,而且是一種新型的軟件。

這種"互聯網軟件"采用客戶端/服務器模式,建立在分布式體系上,通過互聯網通信,具有高延時(high latency)、高並發等特點。

網站開發,完全可以采用軟件開發的模式。但是傳統上,軟件和網絡是兩個不同的領域,很少有交集;軟件開發主要針對單機環境,網絡則主要研究系統之間的通信。互聯網的興起,使得這兩個領域開始融合,現在我們必須考慮,如何開發在互聯網環境中使用的軟件。

RESTful架構,就是目前最流行的一種互聯網軟件架構。它結構清晰、符合標准、易於理解、擴展方便,所以正得到越來越多網站的采用。

一、起源

REST這個詞,是Roy Thomas Fielding在他2000年的博士論文中提出的。

Fielding是一個非常重要的人,他是HTTP協議(1.0版和1.1版)的主要設計者、Apache服務器軟件的作者之一、Apache基金會的第一任主席。

在Fielding的這篇名為Architectural Styles and the Design of Network-based Software Architectures的博士論文(中文版名為《架構風格與基於網絡的軟件架構設計》)中,提出了一整套基於網絡的軟件(即所謂的“分布式應用”)的設計方法。

他的這篇論文一經發表,就引起了關注,並且立即對互聯網開發產生了深遠的影響。

他這樣介紹論文的寫作目的:

"本文研究計算機科學兩大前沿----軟件和網絡----的交叉點。長期以來,軟件研究主要關注軟件設計的分類、設計方法的演化,很少客觀地評估不同的設計選擇對系統行為的影響。而相反地,網絡研究主要關注系統之間通信行為的細節、如何改進特定通信機制的表現,常常忽視了一個事實,那就是改變應用程序的互動風格比改變互動協議,對整體表現有更大的影響。我這篇文章的寫作目的,就是想在符合架構原理的前提下,理解和評估以網絡為基礎的應用軟件的架構設計,得到一個功能強、性能好、適宜通信的架構。"

(This dissertation explores a junction on the frontiers of two research disciplines(研究科學) in computer science: software and networking. Software research has long been concerned with(關注於) the categorization of software designs and the development of design methodologies, but has rarely been able to objectively(客觀地) evaluate(評價) the impact of various design choices on system behavior. Networking research, in contrast, is focused on the details of generic communication behavior between systems and improving the performance of particular communication techniques, often ignoring the fact that changing the interaction(互動,相互影響) style of an application can have more impact on performance than the communication protocols used for that interaction. My work is motivated(激發) by the desire to understand and evaluate the architectural design of network-based application software through principled use of architectural constraints, thereby obtaining the functional, performance, and social properties desired of an architecture. )

二、概念

Fielding將他對互聯網軟件的架構原則,定名為REST,即Representational State Transfer的縮寫。我對這個詞組的翻譯是"表現層狀態轉化"。

如果一個架構符合REST原則,就稱它為RESTful架構。

要理解RESTful架構,最好的方法就是去理解Representational State Transfer這個詞組到底是什么意思,它的每一個詞代表了什么涵義。如果你把這個名稱搞懂了,也就不難體會REST是一種什么樣的設計。

1、資源(Resources)

REST的名稱"表現層狀態轉化"中,省略了主語。"表現層"其實指的是"資源"(Resources)的"表現層"。

資源是一種看待服務器的方式,即,將服務器看作是由很多離散的資源組成。每個資源是服務器上一個可命名的抽象概念,它可以是一段文本、一張圖片、一首歌曲、一種服務。資源是以名詞為核心來組織的,首先關注的是名詞。一個資源可以由一個或多個URI(統一資源定位符)來標識,URI既是資源的名稱,也是資源在Web上的地址。

所謂"上網",就是與互聯網上一系列的"資源"互動,調用它的URI。嚴格地說,有些網址最后的".html"后綴名是不必要的,因為這個后綴名表示格式,屬於"表現層"范疇,而URI應該只代表"資源"的位置。

2、表現層(Representation)

我們把"資源"具體呈現出來的形式,叫做它的Representation,是一段對於資源在某個特定時刻的狀態的描述。可以有多種格式,例如HTML/XML/JSON/純文本/二進制格式/圖片/視頻/音頻等等。它的具體表現形式,應該在HTTP請求的頭信息中用Accept和Content-Type字段指定,這兩個字段是對Representation的描述。

3、狀態轉移(State Transfer)

訪問一個網站,就代表了客戶端和服務器的一個互動過程——在客戶端和服務器端之間轉移(transfer)代表資源狀態的表述。而互聯網通信協議HTTP協議,是一個無狀態協議,所以所有的狀態都需保存在服務器端。

REST要求,必須通過統一的接口來對資源執行各種操作,對於每個資源只能執行一組有限的操作。

以HTTP/1.1協議為例,HTTP/1.1協議定義了一個操作資源的統一接口,主要包括以下內容:

  • 7個HTTP方法:GET/POST/PUT/DELETE/PATCH(在服務器更新資源,客戶端提供改變的屬性)/HEAD(獲取資源的元數據)/OPTIONS(獲取信息,關於資源的哪些屬性是客戶端可以改變的)(分別進行CRUD操作)

  • HTTP頭信息(可自定義)

  • HTTP響應狀態代碼(可自定義)

  • 一套標准的內容協商機制

  • 一套標准的緩存機制

  • 一套標准的客戶端身份認證機制

這時候HTTP頭和有效載荷都包含業務邏輯,例如HTTP方法對應CRUD操作,HTTP狀態碼對應操作結果的狀態。

4、綜述

綜合上面的解釋,我們總結一下Representational State Transfer

  (1)每一個URI代表一種資源;資源表示一種實體,所以應該是名詞(動詞應該放在HTTP協議中)。

  (2)客戶端和服務器之間,轉移這種資源的某種表現層;

  (3)客戶端通過統一的接口,對服務器端資源進行操作,實現"表現層狀態轉移。

三、HATEOAS (The Hypermedia As The Engine Of Application Statue)— 超媒體驅動

2008年10月Fielding寫了一篇博 客,做出了一個非常明確的斷言:REST APIs must be hypertext-driven!

將Web應用看作是一個由很多狀態(應用狀態)組成的有限狀態機。資源之間通過超鏈接相互關聯,超鏈接既代表資源之間的關系,也代表可執行的狀態遷移。在超媒體之中不僅僅包含數據,還包含了狀態遷移的語義。以超媒體作為引擎,驅動Web應用的狀態遷移。通過超媒體暴露出服務器所提供的資源,服務器提供了哪些資源是在運行時通過解析超媒體發現的,而不是事先定義的。

它的重要性在於打破了客戶端和服務器之間嚴格的契約,使得客戶端可以更加智能和自適應,client不用事先知道服務或者工作流中不同步驟,不用再為不同的資源硬編碼URI了;而 REST 服務本身的演化和更新也變得更加容易。 

Github的API就是這種設計,訪問api.github.com會得到一個所有可用API的網址列表。


{ "current_user_url": "https://api.github.com/user", "authorizations_url": "https://api.github.com/authorizations",  // ... } 

從上面可以看到,如果想獲取當前用戶的信息,應該去訪問api.github.com/user,然后就得到了下面結果。


{ "message": "Requires authentication", "documentation_url": "https://developer.github.com/v3" } 

文檔中有一個link屬性。rel表示這個API與當前網址的關系(collection關系,並給出該collection的網址),href表示API的路徑,title表示API的標題,type表示返回類型。


{"link": { "rel": "collection https://www.example.com/zoos", "href": "https://api.example.com/zoos", "title": "List of zoos", "type": "application/vnd.yourformat+json" }} 

訂購一杯咖啡所需要的點單、付款、取咖啡:

GET https://api.example.com/trades
{
  "coffee_trade_url": "https://api.example.com/ trade/coffee",

  "tea_trade_url": "https://api.example.com/trade/tea"
}

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

{

  "trade_coffee_order_url":"https://api.example.com/order/coffee",

  "trade_coffee_payment_url":"https://api.example.com/payment/coffee",

  "trade_coffee_supply_url":"","https://api.example.com/supply/coffee",

}

下載媒體文件:

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"
  }
}

4.REST架構描述

REST架構風格的推導過程如下圖所示:

圖1:REST所繼承的架構風格約束

在圖1中,每一個橢圓形里面的縮寫詞代表了一種架構風格,而每一個箭頭邊的單詞代表了一種架構約束。

REST架構風格最重要的架構約束有6個:

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

 在論文中推導出的REST架構風格如下圖所示:

 圖2:REST架構風格

而HTTP/1.1協議作為一種REST架構風格的架構實例,其架構如下圖所示:

圖3:一個基於REST的架構的過程視圖

 

用戶代理處在三個並行交互(a、b、c)中間,用戶代理的客戶端連接器緩存無法滿足請求,因此它根據每個資源標識符的屬性和客戶端連接器的配置,將每個請求路由到資源的來源。

請求(a)被發送到一個本地代理,代理隨后訪問一個通過DNS查找發現的緩存網關,該網關將這個請求轉發到一個能夠滿足該請求的來源服務器,服務器的內部資源由一個封裝過的對象請求代理(object request broker)架構來定義。

請求(b)直接發送到一個來源服務器,它能夠通過自己的緩存來滿足這個請求。

請求(c)被發送到一個代理,它能夠直接訪問WAIS(Wide Area Information System,一種與Web架構分離的信息服務),並將WAIS的響應翻譯為一種通用的連接器接口能夠識別的格式。

“HTTP不是RPC”、“HTTP不是一種傳輸協議”

5.常見的分布式應用架構橫向對比

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

  • 分布式對象(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與DO的差別在於:

  • REST支持抽象(即建模)的工具是資源DO支持抽象的工具是對象。在不同的編程語言中,對象的定義有很大差別,所以DO風格的架構通常都是與某種編程語言綁定的。跨語言交互即使能實現,實現起來也會非常復雜。而REST中的資源,則完全中立於開發平台和編程語言,可以使用任何編程語言來實現。

  • DO中沒有統一接口的概念。不同的API,接口設計風格可以完全不同。DO也不支持操作語義對於中間組件的可見性。

  • DO中沒有使用超文本,響應的內容中只包含對象本身。REST使用了超文本,可以實現更大粒度的交互,交互的效率比DO更高。

  • REST支持數據流和管道,DO不支持數據流和管道。

  • DO風格通常會帶來客戶端與服務器端的緊耦合。在三種架構風格之中,DO風格的耦合度是最大的,而REST的風格耦合度是最小的。REST松耦合的源泉來自於統一接口+超文本驅動。

REST與RPC的差別在於:

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

  • RPC中沒有統一接口的概念。不同的API,接口設計風格可以完全不同。RPC也不支持操作語義對於中間組件的可見性。

  • RPC中沒有使用超文本,響應的內容中只包含消息本身。REST使用了超文本,可以實現更大粒度的交互,交互的效率比RPC更高。

  • REST支持數據流和管道,RPC不支持數據流和管道。

  • 因為使用了平台中立的消息,RPC風格的耦合度比DO風格要小一些,但是RPC風格也常常會帶來客戶端與服務器端的緊耦合。支持統一接口+超文本驅動的REST風格,可以達到最小的耦合度。

6.總結

REST風格的架構所具有的6個的主要特征:

  • 面向資源(Resource Oriented)

  • 可尋址(Addressability)

  • 連通性(Connectedness)

  • 無狀態(Statelessness)

  • 統一接口(Uniform Interface)

  • 超文本驅動(Hypertext Driven)

面向資源是REST最明顯的特征,即,REST架構設計是以資源抽象為核心展開的;可尋址是指每一個資源在Web之上都有自己的地址;連通性即應該盡量避免設計孤立的資源,除了設計資源本身,還需要設計資源之間的關聯關系,並且通過超鏈接將資源關聯起來。這6個特征是REST架構設計優秀程度的判斷標准。

比較了三種常見的分布式應用架構風格之間的差別后,從面向實用的角度來看,REST架構風格可以為Web開發者帶來三方面的利益:

  • 簡單性——采用REST架構風格,對於開發、測試、運維人員來說,都會更簡單。可以充分利用大量HTTP服務器端和客戶端開發庫、Web功能測試/性能測試工具、HTTP緩存、HTTP代理服務器、防火牆。這些開發庫和基礎設施早已成為了日常用品,不需要什么火箭科技(例如神奇昂貴的應用服務器、中間件)就能解決大多數可伸縮性方面的問題。
  • 可伸縮性——充分利用好通信鏈各個位置的HTTP緩存組件,可以帶來更好的可伸縮性。其實很多時候,在Web前端做性能優化,產生的效果不亞於僅僅在服務器端做性能優化,但是HTTP協議層面的緩存常常被一些資深的架構師完全忽略掉。
  • 松耦合——統一接口+超文本驅動,帶來了最大限度的松耦合。允許服務器端和客戶端程序在很大范圍內,相對獨立地進化。對於設計面向企業內網的API來說,松耦合並不是一個很重要的設計關注點。但是對於設計面向互聯網的API來說,松耦合變成了一個必選項,不僅在設計時應該關注,而且應該放在最優先位置。

七、設計

1.API與用戶的通信協議,總是使用HTTPs協議

2.應該盡量將API部署在專用域名之下。

https://api.example.com 

可以將API的版本號放入URL(另一種做法是,將版本號放在HTTP頭信息中,但不如放入URL方便和直觀)。Github采用這種做法。

https://api.example.com/v1/ 

3.endpoint,表示API的具體網址。

在RESTful架構中,每個網址代表一種資源(resource),所以網址中不能有動詞,只能有名詞,而且所用的名詞往往與數據庫的表格名對應。一般來說,數據庫中的表都是同種記錄的"集合"(collection),所以API中的名詞也應該使用復數

舉例來說,有一個API提供動物園(zoo)的信息,還包括各種動物和雇員的信息,則它的路徑應該設計成下面這樣。

  • https://api.example.com/v1/zoos
  • https://api.example.com/v1/animals
  • https://api.example.com/v1/employees

4.HTTP動詞

下面是一些例子。

  • GET /zoos:列出所有動物園
  • POST /zoos:新建一個動物園
  • GET /zoos/ID:獲取某個指定動物園的信息
  • PUT /zoos/ID:更新某個指定動物園的信息(提供該動物園的全部信息)
  • PATCH /zoos/ID:更新某個指定動物園的信息(提供該動物園的部分信息)
  • DELETE /zoos/ID:刪除某個動物園
  • GET /zoos/ID/animals:列出某個指定動物園的所有動物
  • DELETE /zoos/ID/animals/ID:刪除某個指定動物園的指定動物

針對不同操作,服務器向用戶返回的結果應該符合以下規范。

  • GET /collection:返回資源對象的列表(數組)
  • GET /collection/resource:返回單個資源對象
  • POST /collection:返回新生成的資源對象
  • PUT /collection/resource:返回完整的資源對象
  • PATCH /collection/resource:返回完整的資源對象
  • DELETE /collection/resource:返回一個空文檔

5.過濾信息(Filtering)

如果記錄數量很多,服務器不可能都將它們返回給用戶。API應該提供參數,過濾返回結果。

下面是一些常見的參數。

  • ?limit=10:指定返回記錄的數量
  • ?offset=10:指定返回記錄的開始位置。
  • ?page=2&per_page=100:指定第幾頁,以及每頁的記錄數。
  • ?sortby=name&order=asc:指定返回結果按照哪個屬性排序,以及排序順序。
  • ?animal_type_id=1:指定篩選條件

參數的設計允許存在冗余,即允許API路徑和URL參數偶爾有重復。比如,GET /zoo/ID/animals 與 GET /animals?zoo_id=ID 的含義是相同的。

6.狀態碼(Status Codes)

服務器向用戶返回的狀態碼和提示信息,常見的有以下一些(方括號中是該狀態碼對應的HTTP動詞)。

  • 200 OK - [GET]:服務器成功返回用戶請求的數據,該操作是冪等的(Idempotent)。
  • 201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數據成功。
  • 202 Accepted - [*]:表示一個請求已經進入后台排隊(異步任務)
  • 204 NO CONTENT - [DELETE]:用戶刪除數據成功。
  • 400 INVALID REQUEST - [POST/PUT/PATCH]:用戶發出的請求有錯誤,服務器沒有進行新建或修改數據的操作,該操作是冪等的。
  • 401 Unauthorized - [*]:表示用戶沒有權限(令牌、用戶名、密碼錯誤)。
  • 403 Forbidden - [*] 表示用戶得到授權(與401錯誤相對),但是訪問是被禁止的。
  • 404 NOT FOUND - [*]:用戶發出的請求針對的是不存在的記錄,服務器沒有進行操作,該操作是冪等的。
  • 406 Not Acceptable - [GET]:用戶請求的格式不可得(比如用戶請求JSON格式,但是只有XML格式)。
  • 410 Gone -[GET]:用戶請求的資源被永久刪除,且不會再得到的。
  • 422 Unprocesable entity - [POST/PUT/PATCH] 當創建一個對象時,發生一個驗證錯誤。
  • 500 INTERNAL SERVER ERROR - [*]:服務器發生錯誤,用戶將無法判斷發出的請求是否成功。

狀態碼的完全列表參見這里

如果狀態碼是4xx,就應該向用戶返回出錯信息。一般來說,返回的信息中將error作為鍵名,出錯信息作為鍵值即可。

{ error: "Invalid API key" } 

6.其他

(1)API的身份認證應該使用OAuth 2.0框架。

(2)服務器返回的數據格式,應該盡量使用JSON,避免使用XML。

 

原文有下:

理解RESTful架構 - http://www.ruanyifeng.com/blog/2011/09/restful.html

RESTful API 設計指南 - http://www.ruanyifeng.com/blog/2014/05/restful_api.html

什么才是真正的 RESTful 架構 - http://blog.csdn.net/lz0426001/article/details/52370193

理解本真的REST架構風格 - http://www.infoq.com/cn/articles/understanding-restful-style/

使用 Spring HATEOAS 開發 REST 服務 - https://www.ibm.com/developerworks/cn/java/j-lo-SpringHATEOAS/

其他參考資料:

Roy Fielding博士論文英文版

Roy Fielding博士論文中文版

HTTP/1.1協議RFC2616RFC2617


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM