1. 程式人生 > >淺談HTTP RESTful架構

淺談HTTP RESTful架構

RESTful 是一種非常流行的軟體架構,或者說設計風格而非新的技術標準。提供了一組設計原則和約束條件,主要用於客戶端與伺服器的互動。RESTful架構更簡潔,更有層次,更易於實現快取等機制。

1.理解RESTful

RESTful, 全稱Representational State Transfer。REST通常基於使用HTTP,URI,和XML以及HTML這些現有的廣泛流行的協議和標準。要理解RESTful概念,需要明白下面的概念:

1.1 資源與URI

REST全稱是表述性狀態轉移,表述指的就是資源。資源通過URI(Uniform Resource Identifier)來標示。URI的設計應該遵循可定址性原則,具有自描述性。

這裡以github網站為例,給出一些還算不錯的URI:

  • https://github.com/git
  • https://github.com/git/git/blob/master/block-sha1/sha1.h
  • https://github.com/git/git/pulls
  • https://github.com/git/git/pulls?state=closed

關於URI設計技巧:

  • 使用 _ 或 - 來讓URI可讀性更好,例如http://www.github.com/blog/translate-reward-plan

  • 使用 / 來表示資源的層級關係,例如上面的/git/git/blob/master/block-sha1/sha1.h

  • 使用 ?

     用來過濾資源,例如/git/pulls?state=closed用來表示git專案的所有推入請求中已經關閉的請求。

  • 使用,;表示同級資源關係,例如/git/sha1/compare/ef7b53d18;bd638e8c1

1.2 統一資源介面

RESTful架構應該遵循統一介面原則,統一介面包含了一組受限的預定義的操作,所有資源的訪問介面應該使用標準的HTTP方法如GET,PUT,POST,DELETE,並遵循這些方法的語義。

如果按照HTTP方法的語義來暴露資源,那麼介面將會擁有安全性和冪等性的特性,例如GET和HEAD請求都是安全的, 無論請求多少次,都不會改變伺服器狀態。而GET、HEAD、PUT和DELETE請求都是冪等的,無論對資源操作多少次, 結果總是一樣的,後面的請求並不會產生比第一次更多的影響。

下面列出了GET,DELETE,PUT和POST的典型用法:

GET

安全且冪等 獲取表示 變更時獲取表示(快取) 200(OK) - 表示已在響應中發出 204(無內容) - 資源有空表示 301(Moved Permanently) - 資源的URI已被更新 303(See Other) - 其他(如,負載均衡) 304(not modified)- 資源未更改(快取) 400 (bad request)- 指代壞請求(如,引數錯誤) 404 (not found)- 資源不存在 406 (not acceptable)- 服務端不支援所需表示 500 (internal server error)- 通用錯誤響應 503 (Service Unavailable)- 服務端當前無法處理請求

POST

不安全且不冪等 使用服務端管理的(自動產生)的例項號建立資源 建立子資源 部分更新資源 如果沒有被修改,則不過更新資源(樂觀鎖) 200(OK)- 如果現有資源已被更改 201(created)- 如果新資源被建立 202(accepted)- 已接受處理請求但尚未完成(非同步處理) 301(Moved Permanently)- 資源的URI被更新 303(See Other)- 其他(如,負載均衡) 400(bad request)- 指代壞請求 404 (not found)- 資源不存在 406 (not acceptable)- 服務端不支援所需表示 409 (conflict)- 通用衝突 412 (Precondition Failed)- 前置條件失敗(如執行條件更新時的衝突) 415 (unsupported media type)- 接受到的表示不受支援 500 (internal server error)- 通用錯誤響應 503 (Service Unavailable)- 服務當前無法處理請求

PUT

不安全但冪等 用客戶端管理的例項號建立一個資源 通過替換的方式更新資源 如果未被修改,則更新資源(樂觀鎖) 200 (OK)- 如果已存在資源被更改 201 (created)- 如果新資源被建立 301(Moved Permanently)- 資源的URI已更改 303 (See Other)- 其他(如,負載均衡 ) 400 (bad request)- 指代壞請求 404 (not found)- 資源不存在 406 (not acceptable)- 服務端不支援所需表示 409 (conflict)- 通用衝突 412 (Precondition Failed)- 前置條件失敗(如執行條件更新時的衝突) 415 (unsupported media type)- 接受到的表示不受支援 500 (internal server error)- 通用錯誤響應 503 (Service Unavailable)- 服務當前無法處理請求

DELETE

不安全但冪等 刪除資源 200 (OK)- 資源已被刪除 301 (Moved Permanently)- 資源的URI已更改 303 (See Other)- 其他,如負載均衡 400 (bad request)- 指代壞請求 404 (not found)- 資源不存在 409 (conflict)- 通用衝突 500 (internal server error)- 通用錯誤響應 503 (Service Unavailable)- 服務端當前無法處理請求

接下來再按一些實踐中的常見問題

  • POST和PUT在建立資源的區別:所建立的資源的名稱(URI)是否由客戶端決定。 例如為為部落格增加一個android的分類,生成的路徑就是分類名/categories/android,那麼就可以採用PUT方法。
  • 客戶端不一定都支援這些HTTP方法:較古老的基於瀏覽器的客戶端,只能支援GET和POST兩種方法。妥協的解決方法,通過隱藏引數_method=DELETE來傳遞真實的請求方法等措施來規避。
  • 統一資源介面對URI的意義:統一資源介面要求使用標準的HTTP方法對資源進行操作,所以URI只應該來表示資源的名稱,而不應該包括資源的操作,如下是一些不符合統一介面要求的URI:

    • GET /getUser/1
    • POST /createUser
    • PUT /updateUser/1
    • DELETE /deleteUser/1

    正確寫法應該是 /User/1,不應該包含動詞,具體的動作由請求方法來體現。

1.3 資源的表述

資源的表述是指對資源在特定時刻的狀態的描述,客戶端通過HTTP方法可以獲取資源,更準確說是資源的表述而已。 資源在外界的具體呈現,可以有多種表述形式,在客戶端和服務端之間傳送的也是資源的表述,而不是資源本身。 例如文字資源可以採用html、xml、json等格式,圖片可以使用PNG或JPG展現出來。

資源的表述包括資料和描述資料的元資料,例如,HTTP頭”Content-Type” 就是這樣一個元資料屬性。通過HTTP內容協商,客戶端可以通過Accept頭請求一種特定格式的表述,服務端則通過Content-Type告訴客戶端資源的表述形式。

1.4 資源的連結

REST是使用標準的HTTP方法來操作資源的,但僅僅因此就理解成帶CURD的Web資料庫架構就太過於簡單了。這種反模式忽略了一個核心概念:”超媒體即應用狀態引擎”。 超媒體是什麼?當你瀏覽Web網頁時,從一個連線跳到一個頁面,再從另一個連線跳到另外一個頁面,就是利用了超媒體的概念:把一個個把資源連結起來.

要達到這個目的,就要求在表述格式裡邊加入連結來引導客戶端。在《RESTful Web Services》一書中,作者把這種具有連結的特性成為連通性。下面我們具體來看一些例子。

下面展示的是github獲取某個組織下的專案列表的請求,可以看到在響應頭裡邊增加Link頭告訴客戶端怎麼訪問下一頁和最後一頁的記錄。 而在響應體裡邊,用url來連結專案所有者和專案地址。

get

又例如下面這個例子,建立訂單後通過連結引導客戶端如何去付款。

get

上面的例子展示瞭如何使用超媒體來增強資源的連通性。很多人在設計RESTful架構時,使用很多時間來尋找漂亮的URI,而忽略了超媒體。所以,應該多花一些時間來給資源的表述提供連結,而不是專注於”資源的CRUD”。

1.5 狀態的轉移

REST原則中的無狀態通訊原則,並不是說客戶端應用不能有狀態,而是指服務端不應該儲存客戶端狀態。

應用狀態與資源狀態

客戶端負責維護應用狀態,而服務端維護資源狀態。客戶端與服務端的互動必須是無狀態的,並在每一次請求中包含處理該請求所需的一切資訊。

服務端不需要在請求間保留應用狀態,只有在接受到實際請求的時候,服務端才會關注應用狀態。這種無狀態通訊原則,使得服務端和中介能夠理解獨立的請求和響應。 在多次請求中,同一客戶端也不再需要依賴於同一伺服器,方便實現高可擴充套件和高可用性的服務端。

但有時候我們會做出違反無狀態通訊原則的設計,例如利用Cookie跟蹤某個服務端會話狀態,常見的像J2EE裡邊的JSESSIONID。這意味著,瀏覽器隨各次請求發出去的Cookie是被用於構建會話狀態的。當然,如果Cookie儲存的是一些伺服器不依賴於會話狀態即可驗證的資訊(比如認證令牌),這樣的Cookie也是符合REST原則的。

應用狀態的轉移

狀態轉移到這裡已經很好理解了, “會話”狀態不是作為資源狀態儲存在服務端的,而是被客戶端作為應用狀態進行跟蹤的。客戶端應用狀態在服務端提供的超媒體的指引下發生變遷。服務端通過超媒體告訴客戶端當前狀態有哪些後續狀態可以進入。

這些類似”下一頁”之類的連結起的就是這種推進狀態的作用——指引你如何從當前狀態進入下一個可能的狀態。這些類似”下一頁”之類的連結起的就是這種推進狀態的作用——指引你如何從當前狀態進入下一個可能的狀態。

總結

本文從資源的定義、獲取、表述、關聯、狀態變遷等角度, 試圖快速理解RESTful架構背後的概念。RESTful架構與傳統的RPC、SOAP等方式在理念上有很大的不同,希望本文能對各位理解REST有所幫助。