愈來愈多的人開始意識到,網站即軟件,並且是一種新型的軟件。javascript
這種"互聯網軟件"採用客戶端/服務器模式,創建在分佈式體系上,經過互聯網通訊,具備高延時(high latency)、高併發等特色。html
網站開發,徹底能夠採用軟件開發的模式。可是傳統上,軟件和網絡是兩個不一樣的領域,不多有交集;軟件開發主要針對單機環境,網絡則主要研究系統之間的通訊。互聯網的興起,使得這兩個領域開始融合,如今咱們必須考慮,如何開發在互聯網環境中使用的軟件。前端
RESTful架構,就是目前最流行的一種互聯網軟件架構。它結構清晰、符合標準、易於理解、擴展方便,因此正獲得愈來愈多網站的採用。java
REST這個詞,是Roy Thomas Fielding在他2000年的博士論文中提出的。git
Fielding是一個很是重要的人,他是HTTP協議(1.0版和1.1版)的主要設計者、Apache服務器軟件的做者之1、Apache基金會的第一任主席。github
在Fielding的這篇名爲Architectural Styles and the Design of Network-based Software Architectures的博士論文(中文版名爲《架構風格與基於網絡的軟件架構設計》)中,提出了一整套基於網絡的軟件(即所謂的「分佈式應用」)的設計方法。數據庫
他的這篇論文一經發表,就引發了關注,而且當即對互聯網開發產生了深遠的影響。編程
他這樣介紹論文的寫做目的:json
"本文研究計算機科學兩大前沿----軟件和網絡----的交叉點。長期以來,軟件研究主要關注軟件設計的分類、設計方法的演化,不多客觀地評估不一樣的設計選擇對系統行爲的影響。而相反地,網絡研究主要關注系統之間通訊行爲的細節、如何改進特定通訊機制的表現,經常忽視了一個事實,那就是改變應用程序的互動風格比改變互動協議,對總體表現有更大的影響。我這篇文章的寫做目的,就是想在符合架構原理的前提下,理解和評估以網絡爲基礎的應用軟件的架構設計,獲得一個功能強、性能好、適宜通訊的架構。"api
(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是一種什麼樣的設計。
REST的名稱"表現層狀態轉化"中,省略了主語。"表現層"其實指的是"資源"(Resources)的"表現層"。
資源是一種看待服務器的方式,即,將服務器看做是由不少離散的資源組成。每一個資源是服務器上一個可命名的抽象概念,它能夠是一段文本、一張圖片、一首歌曲、一種服務。資源是以名詞爲核心來組織的,首先關注的是名詞。一個資源能夠由一個或多個URI(統一資源定位符)來標識,URI既是資源的名稱,也是資源在Web上的地址。
所謂"上網",就是與互聯網上一系列的"資源"互動,調用它的URI。嚴格地說,有些網址最後的".html"後綴名是沒必要要的,由於這個後綴名錶示格式,屬於"表現層"範疇,而URI應該只表明"資源"的位置。
咱們把"資源"具體呈現出來的形式,叫作它的Representation,是一段對於資源在某個特定時刻的狀態的描述。能夠有多種格式,例如HTML/XML/JSON/純文本/二進制格式/圖片/視頻/音頻等等。它的具體表現形式,應該在HTTP請求的頭信息中用Accept和Content-Type字段指定,這兩個字段是對Representation的描述。
訪問一個網站,就表明了客戶端和服務器的一個互動過程——在客戶端和服務器端之間轉移(transfer)表明資源狀態的表述。而互聯網通訊協議HTTP協議,是一個無狀態協議,因此全部的狀態都需保存在服務器端。
以HTTP/1.1協議爲例,HTTP/1.1協議定義了一個操做資源的統一接口,主要包括如下內容:
7個HTTP方法:GET/POST/PUT/DELETE/PATCH(在服務器更新資源,客戶端提供改變的屬性)/HEAD(獲取資源的元數據)/OPTIONS(獲取信息,關於資源的哪些屬性是客戶端能夠改變的)(分別進行CRUD操做)
HTTP頭信息(可自定義)
HTTP響應狀態代碼(可自定義)
一套標準的內容協商機制
一套標準的緩存機制
一套標準的客戶端身份認證機制
這時候HTTP頭和有效載荷都包含業務邏輯,例如HTTP方法對應CRUD操做,HTTP狀態碼對應操做結果的狀態。
綜合上面的解釋,咱們總結一下Representational State Transfer:
(1)每個URI表明一種資源;資源表示一種實體,因此應該是名詞(動詞應該放在HTTP協議中)。
(2)客戶端和服務器之間,轉移這種資源的某種表現層;
(3)客戶端經過統一的接口,對服務器端資源進行操做,實現"表現層狀態轉移。
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_
url": "https://api.example.com/
_trade
trade
/tea
"
}
GET https://api.example.com/
_coffeetrade
{
"
_coffee_order_url":"trade
https://api.example.com/
",
coffeeorder
/
"
_coffee_payment_url":"trade
https://api.example.com/
",
coffeepayment/
"
_coffee_supply_url":"","trade
https://api.example.com/
",
coffeesupply/
}
下載媒體文件:
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"
}
}
REST架構風格的推導過程以下圖所示:
圖1:REST所繼承的架構風格約束
在圖1中,每個橢圓形裏面的縮寫詞表明瞭一種架構風格,而每個箭頭邊的單詞表明瞭一種架構約束。
REST架構風格最重要的架構約束有6個:
在論文中推導出的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不是一種傳輸協議」
從架構風格的抽象高度來看,常見的分佈式應用架構風格有三種:
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風格,能夠達到最小的耦合度。
REST風格的架構所具備的6個的主要特徵:
面向資源(Resource Oriented)
可尋址(Addressability)
連通性(Connectedness)
無狀態(Statelessness)
統一接口(Uniform Interface)
超文本驅動(Hypertext Driven)
面向資源是REST最明顯的特徵,即,REST架構設計是以資源抽象爲核心展開的;可尋址是指每個資源在Web之上都有本身的地址;連通性即應該儘可能避免設計孤立的資源,除了設計資源自己,還須要設計資源之間的關聯關係,而且經過超連接將資源關聯起來。這6個特徵是REST架構設計優秀程度的判斷標準。
比較了三種常見的分佈式應用架構風格之間的差異後,從面向實用的角度來看,REST架構風格能夠爲Web開發者帶來三方面的利益:
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/
其餘參考資料: