轉自 廖宇雷的Weblog
REST 是由 Roy Fielding 在他的論文《Architectural Styles and the Design of Network-based Software Architectures》中提出的一個術語。
REST 是英文 Representational State Transfer 的縮寫,有中文翻譯為“具象狀態傳輸”(參考:《SIP/IMS網路中的Representational State Transfer (REST)和資料分布》)。
—————————————
前面的內容比較枯燥,我說說我自己的理解。
但是 REST 到底是什麼呢。論文我看不懂,不過找到一篇更簡單易懂的東西:《Building Web Services the REST Way》。
根據這篇文章,我整理了一下我自己對 REST 的理解:
REST 首先只是一種架構樣式,不是一種標準。這點和 Ajax 類似,兩者都是利用現有的成熟技術。
在 REST 的定義中,一個 Web 應用程式總是使用固定的 URI 向外部世界呈現(或者說暴露)一個資源。
URI 是英文 Uniform Resource Identifier 的縮寫,中文翻譯“通用資源標誌符”。
“通用資源標誌符”是指唯一標識一個資源(xhtml 檔案、圖片、css 樣式表)的字串。當然了,RFC 中定義的 URI 複雜得多,不過我們此處將 URI 想象成一個人的社會安全號碼碼就行了(你不能有兩個同時有效社會安全號碼碼,一個號碼也不可能同時對應兩個人)。而我們天天掛在嘴邊的 URL 地址就是 URI 的一種表現形式(個人理解,有錯請糾正)。
知道什麼是 URI 後,我們來看一個實際例子:
http://www.example.com/photo/logo 指向 example.com 網站(可以視為一個 Web 應用程式)中類型為 photo,名字為 logo 的資源。我們用瀏覽器訪問這個 URI,看到的將可能是一個 xhtml 文檔,其中用 <img src=”……” /> 來顯示實際的照片。
http://www.example.com/photo/logo 很容易讓你想到 URL 重寫。事實上,這個地址很可能會在伺服器內部處理為 http://www.example.com/photo.php?name=logo 這樣的地址。photo.php 是伺服器端的一個動態指令檔,根據 name 參數產生 xhtml 文檔返回給瀏覽器。
現在假設我們要擷取這張照片的 XML 文檔。XML 文檔中包含照片的檔案名稱、檔案大小、拍攝日期等等資訊。也就是說我們要擷取“同一個資源的不同表現形式的資料”。對於這個要求,我們可以很容易的用另一個 URL 地址達到:http://www.example.com/xml/logo。
但是,這就違背了“URI 唯一標識一個資源”的定義。如果我們要擷取同一個資源的多種表現形式,那麼就要使用更多的 URL,從而給一個資源指定了多個不同的 URI。
而在 REST 中,不管是擷取照片的 xhtml 文檔還是 XML 文檔,或者照片檔案本身,都是用同一個 URI,就是 http://www.example.com/photo/logo。
那這是怎麼辦到的呢。Ruby On Rails 中是通過分辨 HTTP Request Header 資訊來分辨用戶端是想要取得資源的哪一種表現形式的資料。
當我們用瀏覽器訪問一個網址時,瀏覽器會構造一個 HTTP 要求。這個請求有一個頭資訊,其中包括了本次請求接受何種類型的資料。通常瀏覽器發送的 HTTP 要求頭中,Accept 的值都是 */*,也就說接受伺服器返回的任何類型的資料。
看到這裡,聰明的傢伙應該知道了。只要我們指定一個特定的 Accept 參數,那麼伺服器就可以通過判斷該參數來決定返回什麼類型的資料。所以在一個採用 REST 架構的應用中,要擷取同一個資源的不同表現形式的資料,只需要使用不同的 HTTP 要求頭資訊就行了。
如果考慮為 Web 應用程式增加 Web Services,這種技術的價值就體現出來了。比如我寫了一個 Delphi 程式,現在只需要構造一個包含 Accept: text/xml 的 HTTP 要求頭,然後將請求發送到 http://www.example.com/photo/logo 就可以了。返回的結果就是一個 XML 文檔,而不是 xhtml 文檔。
因為我們的 HTTP 要求頭資訊有不同的狀態,從而可以獲得不同的資料,所以叫做“具象狀態傳輸”
—————————————
除了上面的用法,REST 還有進一步的擴充。
我們在 Web 應用程式中處理來自用戶端的請求時,通常只考慮 GET 和 POST 這兩種 HTTP 要求方法。實際上,HTTP 還有 HEAD、PUT、DELETE 等要求方法。而在 REST 架構中,用不同的 HTTP 要求方法來處理對資源的 CRUD(建立、讀取、更新和刪除)操作: POST: 建立 GET: 讀取 PUT: 更新 DELETE: 刪除
經過這樣的一番擴充,我們對一個資源的 CRUD 操作就可以通過同一個 URI 完成了:
http://www.example.com/photo/logo(讀取)
仍然保持為 [GET] http://www.example.com/photo/logo
http://www.example.com/photo/logo/create(建立)
改為 [POST] http://www.example.com/photo/logo
http://www.example.com/photo/logo/update(更新)
改為 [PUT] http://www.example.com/photo/logo
http://www.example.com/photo/logo/delete(刪除)
改為 [DELETE] http://www.example.com/photo/logo
從而進一步規範了資源標識的使用。
通過 REST 架構,Web 應用程式可以用一致的介面(URI)暴露資源給外部世界,並提供對資源的操作服務。這對於以資源為中心的 Web 應用程式來說非常重要。例如照片分享網站、使用者社區等。
—————————————
Ruby On Rails 1.2 版對 REST 有很好的支援,但要在 PHP 中應用 REST 還需要解決不少問題: 如何在服務端判斷 PUT、DELETE 要求方法; 如何擷取用 PUT、DELETE 要求方法中傳遞的資料; 如何擷取 HTTP 要求頭資訊中的 Accept 參數值; 如何在瀏覽器端發起 PUT 和 DELETE 請求。
不過我仔細看了 PHP 文檔,我覺得上面幾個問題都是可以解決的。
服務端綜合使用 $_SERVER[’HTTP_ACCEPT’]、$_SERVER[’REQUEST_URI’]、$_SERVER[’REQUEST_METHOD’]、$_SERVER[’QUERY_STRING’] 這些變數應該可以搞定前面三個問題。而第四個問題則可以用 JavaScript 的 XMLHttpRequest 對象來實現。
不過我想 REST 的真正價值在於 Web Services,而不是通過瀏覽器操作的應用程式。