簡單識別 RESTful 接口
本文描述了識別一個接口是否真的是 RESTful 接口的基本方法。符合 REST 架構風格的接口,稱為 RESTful 接口。本文不打算從架構風格的推導方面描述,而是從 HTTP 標準的方面描述。識別的方法同時也是指導實踐的原則。
一、是否使用了正確(合適)的方法
目前對于 HTTP 標準濫用較多的,就是方法。談起 RESTful 接口的方法,很多資料告訴大家,說 GET、POST、PUT、DELETE,分別對應數據庫操作的 SELECT、INSERT、UPDATE、DELETE。其實這種理解是不正確的。
方法有兩個性質:安全性與冪等性;以及一個語義:動作本身的意思。RESTful 接口為各種數據抽象為資源,對資源的操作,主要是依據方法的兩個性質,其次才是語義。
GET 方法的語義是獲取資源,性質是安全、冪等。也就是說,對資源進行安全冪等的操作,應該用 GET 或 HEAD,當目的是獲取資源時,選用 GET,目的是獲取資源的元數據時,使用 HEAD。使用安全冪等的方法執行不安全或不冪等的操作,都是錯誤的。一個典型的例子是:
GET /book/357?op=delete
這個例子的意圖是刪除 ID 為 357 的 book,DELETE 是不安全的操作,卻使用了 GET 這個安全冪等的操作。這作法的副作用是可能被緩存,所有組件(包括搜索引擎)都認為它是安全的,從而可能不加任何限制地對它進行訪問。
POST 方法的語義是提交資源。“提交”本身就是一個比較抽象的動詞,即它的語義是可以根據環境變化的,如果將它與 INSERT 對應起來,是以偏概全的。它的性質是不安全、不冪等。這意味著所有組件對它都不緩存,不重復發送請求。執行不安全、不冪等的操作,唯有選擇 POST 方法(標準未規定的所有自定義方法,在性質上等同于 POST)。
一個不安全、不冪等的方法,可以執行安全、冪等的操作,前提是需要犧牲緩存等屬性。比如提交一個很多參數的查詢,可以使用 POST。
HTTP/1.1標準那么多年,HTML標準到5了,為什么HTML的 form 只有 GET 和 POST 兩種 action ?為沒有不把 PUT、DELETE、PATCH 等方法加進去?其實是有原因的。
首先,form 元素的提交表單的按鈕是 submit,與 POST 的語義是等同的。
其次,當瀏覽器獲得一個資源時,form 只是資源的一部分,不能表示整個資源,根據 PUT/DELETE/PATCH 的定義,它無法表示它們。
由于 POST 的性質與抽象的語義,在沒有合適的方法時,都可以使用 POST 代替。因此,將 POST 等同于新增/插入的那些文章或教材,都在誤導人。
篇幅有限,其它方法不說了,網上基于其它方法的文章基本正確的。
二、是否支使媒體協商
媒體協商是指客戶端和服務器對某種媒體類型的處理能力。一般情況下,客戶端并不知道服務器能處理哪種媒體。瀏覽器就是典型代表,它在無先驗知識的情況下,會進行媒體探測:
GET / HTTP/1.1 Host: example.com Accept: text/html; q=1.0, image/*; q=0.8, */*; q=0.1
這是告訴服務器,我能非常有效地處理 HTML 類型的資源,圖片也是很有效的,其它的資源類型,我也能接受。于是服務器就優先按 HTML 返回資源,如果沒有 HTML 或 Image,就返回服務器自身最合適的,如 application/json。
假設服務器僅支持 application/json,當客戶端要求一個 application/xml 時,應當返回 415 Unsupported Media Type,并在正文中說明支持哪種媒體類型。
舉個值得學習的案例,Github:https://api.github.com/ ,大家可以自己試試。
三、是否能夠進行狀態轉移
這一點非常重要,也是 REST 的本意,狀態轉移!HTTP 標準實現的 REST 中,連接(Link)是實現狀態轉移的重要組件(HTML 的超鏈接和表單也是)。
在無先驗知識的前提下,客戶端請求資源 / ,這個資源及其元數據,要能夠為應用程序的下一個狀態的轉移提供必要的連接。有時候甚至要提供文檔說明的連接。
例如,當我們訪問 https://api.github.com/ 時,我們看到一個連接的列表。通過這些連接,我們的客戶端就可以跳轉到另一個狀態,從而實現整個應用程序的狀態轉移。狀態如何更好的轉移(甚至是自動化的狀態轉移),就要依賴于連接的關系(rel)以及連接的媒體類型(type)和可選的文檔。
這種狀態轉移的能力,正是 REST 的魅力。誠然,最成功的 REST 客戶端是瀏覽器,最成功的媒體類型是 text/html,最成功的 REST 是 HTTP,通過 URI 標準構建的連接,進化成我們今天無法量化的巨大的分布式系統:Web。
額外一點,一個 RESTful 的接口是不需在路徑或頭部字段中使用接口的版本號的。RESTful 接口這種說法,也有誤。REST有統一接口的概念,但這個概念一般是指 HTTP 標準。在一個 RESTful 的環境中,一切事物都是資源,資源是沒有版本號的。版本號是 API 的概念,API 是 RPC 的事物。
資源一旦發布,其結構就基本上不再發生變化。唯一能夠變化的資源結構是在對舊資源無副作用的前提下,新增資源的字段或屬性。當新增或刪除的屬性與當前資源的結構不兼容時,則需要增加一個或一類新的資源。因此,在 RESTful 實踐中,是不需要使用版本號來標識資源的。
當然,RESTful 內容的豐富,遠遠不止以上所提到的三點。但這三點是非常基本的要求。