表现层状态转换

**表現層狀態轉換**（英語：**Representational State Transfer**，縮寫：**REST**）是 Roy Thomas Fielding 博士於 2000 年在他的博士論文中提出來的一種全球資訊網軟體架構風格，目的是便於不同軟體 / 程式在網路（例如網際網路）中互相傳遞資訊。表現層狀態轉換是根基於超文字傳輸協定（HTTP）之上而確定的一組約束和屬性，是一種設計提供全球資訊網絡服務的軟體構建風格。符合或相容於這種架構風格（簡稱為 REST 或 RESTful）的網路服務，允許使用者端發出以統一資源標識符存取和操作網路資源的請求，而與預先定義好的無狀態操作集一致化。因此表現層狀態轉換提供了在網際網路的計算系統之間，彼此資源可互動使用的協作性質（interoperability）。相對於其它種類的網路服務，例如 SOAP 服務，則是以本身所定義的操作集，來存取網路上的資源。

目前在三種主流的 Web 服務實現方案中，因為 REST 模式與複雜的 SOAP 和 XML-RPC 相比更加簡潔，越來越多的 Web 服務開始採用 REST 風格設計和實現。例如，Amazon.com 提供接近 REST 風格的 Web 服務執行圖書查詢；Yahoo 提供的 Web 服務也是 REST 風格的。

## 要點及標準

需要注意的是，REST 是設計風格而**不是**標準。REST 通常基於 HTTP、URI、XML 以及 HTML 這些現有的廣泛流行的協定和標準。

* 資源是由 URI 來指定。
* 對資源的操作包括取得、建立、修改和刪除，這些操作正好對應 HTTP 協定提供的 GET、POST、PUT 和 DELETE 方法。
* 通過操作資源的表現形式來操作資源。
* 資源的表現形式則是 XML 或者 HTML，取決於讀者是機器還是人、是消費 Web 服務的客戶軟體還是 Web 瀏覽器。當然也可以是任何其他的格式，例如 JSON。

### 表示狀態傳送的特徵

* Uniform Interface：統一介面。
  1. **以資源為基礎**每個資源都可以通過 URI 存取到。
  
  也就是一個個可以認知的資源，比如文件，音樂，影片等資訊，都可以通過唯一的 URI 確定。
  2. **通過重表達的客戶端可以管理原資源**就是我們通過客戶端可以修改原資源的狀態。
  3. **返回資訊足夠描述自己**這樣重表達的客戶端可以知道如何處理。
  4. **超媒體是應用狀態的引擎**處理以超媒體為基礎的狀態變化。
* Stateless：無狀態。
* Cacheable：可快取。
* Client-Server：客戶伺服器分離模式，任何一個客戶端與伺服器都是可替換的。
* Layered System：分層的系統，客戶端不知道他聯絡的是不是最終伺服器。
* Code on Demand（可選）：伺服器可以將能力擴充到客戶端，如果客戶端可以執行的話。這個功能是可選擇的。

### REST 架構的限制條件

REST 架構風格最重要的架構限制有 6 個：

1. **客戶端 - 伺服器（Client-Server）** 
  * 客戶端 - 伺服器結構限制的目的是將客戶端和伺服器端的關注點分離。將使用者介面所關注的邏輯和資料儲存所關注的邏輯分離開來有助於提高使用者介面的跨平台的可移植性。通過簡化伺服器模組也有助於伺服器模組的可延伸性。
2. **無狀態（Stateless）** 
  * 伺服器不能儲存客戶端的資訊；每一次從客戶端傳送的請求中，要包含所有的必須的狀態資訊，對談資訊由客戶端儲存，伺服器端根據這些狀態資訊來處理請求。
  * 伺服器可以將對談狀態資訊傳遞給其他服務，比如資料庫服務，這樣可以保持一段時間的狀態資訊，從而實現認證功能。
  * 當客戶端可以切換到一個新狀態的時候傳送請求資訊。
  * 當一個或者多個請求被傳送之後，客戶端就處於一個狀態變遷過程中。每一個應用的狀態描述可以被客戶端用來初始化下一次的狀態變遷。
3. **快取（Cacheability）** 
  * 如同全球資訊網一樣，客戶端和中間的通訊傳遞者可以將回覆快取起來。回覆必須明確的或者間接的表明本身是否可以進行快取，這可以預防客戶端在將來進行請求的時候得到陳舊的或者不恰當的資料。管理良好的快取機制可以減少客戶端 - 伺服器之間的互動，甚至完全避免客戶端 - 伺服器互動，這進一步提了高效能和可延伸性。
4. **統一介面（Uniform Interface）** 
  * 這是 RESTful 系統設計的基本出發點。它簡化了系統架構，減少了耦合性，可以讓所有模組各自獨立的進行改進。包括下列四個限制：
    1. **請求中包含資源的 ID（Resource identification in requests）** 請求中包含了各種獨立資源的標識，例如，在 Web 服務中的 URI。資源本身和傳送給客戶端的標識是獨立。例如，伺服器可以將自身的資料庫資訊以 HTML、XML 或者 JSON 的方式傳送給客戶端，但是這些可能都不是伺服器的內部記錄方式。
    2. **資源通過標識來操作（Resource manipulation through representations）** 當客戶端擁有一個資源的標識，包括附帶的元資料，則它就有足夠的資訊來刪除這個資源。
    3. **訊息的自我描述性（Self-descriptive messages）** 每一個訊息都包含足夠的資訊來描述如何來處理這個資訊。例如，媒體類型 (media-type) 就可以確定需要什麼樣的剖析器來分析媒體資料.
    4. **用超媒體驅動應用狀態（Hypermedia as the engine of application state (HATEOAS)）** 同使用者存取 Web 伺服器的 Home 頁面相似，當一個 REST 客戶端存取了最初的 REST 應用的 URI 之後，REST 客戶端應該可以使用伺服器端提供的連結，動態的發現所有的可用的資源和可執行的操作。隨著存取的進行，伺服器在回應中提供文字超連結，以便客戶端可以得到當前可用的操作。客戶端無需用確定的編碼的方式記錄下伺服器端所提供的動態應用的結構資訊。
5. **分層系統（Layered System）** 
  * 客戶端一般不知道是否直接連接到了最終的伺服器，或者是路徑上的中間伺服器。中間伺服器可以通過負載均衡和共享快取的機制提高系統的可延伸性，這樣可也便於安全策略的部署。
6. **按需代碼（Code-On-Demand，可選）** 
  * 伺服器可以通過傳送可執行代碼給客戶端的方式臨時性的擴充功能或者客製化功能，例如 Java Applet、Flash 或 JavaScript。

### 關於狀態

應該注意區別應用的狀態和連接協定的狀態。HTTP 連接是無狀態的（也就是不記錄每個連接的資訊），而 REST 傳輸會包含應用的所有狀態資訊，因此可以大幅降低對 HTTP 連接的重複請求資源消耗。

### 應用於 Web 服務

符合 REST 設計風格的 Web API 稱為 **RESTful API**。它從以下三個方面資源進行定義：

* 直觀簡短的資源位址：URI，比如：`http://example.com/resources`。
* 傳輸的資源：Web 服務接受與返回的網際網路媒體類型，比如：JSON，XML，YAML 等。
* 對資源的操作：Web 服務在該資源上所支援的一系列請求方法（比如：POST，GET，PUT 或 DELETE）。

下表列出了在實現 RESTful API 時 HTTP 請求方法的典型用途。

PUT 和 DELETE 方法是冪等方法。GET 方法是安全方法（不會對伺服器端有修改，因此當然也是冪等的）。

不像基於 SOAP 的 Web 服務，RESTful Web 服務並沒有「正式」的標準。這是因為 REST 是一種架構，而 SOAP 只是一個協定。雖然 REST 不是一個標準，但大部分 RESTful Web 服務實現會使用 HTTP、URI、JSON 和 XML 等各種標準。

## 實現舉例

例如，一個簡單的網路商店應用：

1. 列舉所有商品：
```
GET http://www.store.com/products
```
2. 呈現某一件商品：
```
GET http://www.store.com/products/12345
```
3. 下單購買：
```
POST http://www.store.com/orders
<purchase-order>
  <item> ... </item>
</purchase-order>
```

## REST 的優點

* 可更高效利用快取來提高回應速度
* 通訊本身的無狀態性可以讓不同的伺服器的處理一系列請求中的不同請求，提高伺服器的擴充性
* 瀏覽器即可作為客戶端，簡化軟體需求
* 相對於其他疊加在 HTTP 之上的機制，REST 的軟體相依性更小
* 不需要額外的資源發現機制
* 在軟體技術演進中的長期的相容性更好

## 實現

* Ruby on Rails1.2 以後的版本支援 REST model （頁面存檔備份，存於網際網路檔案館）。
* JBoss RESTEasyJBoss 的 REST 實現
* Node.js RESTful APINode.js 實現 RESTful API
* Learn REST: A RESTful Tutorial （頁面存檔備份，存於網際網路檔案館）

## 參考文獻

### 參照

### 來源