什么是 REST
REST 一詞,是由 HTTP 協議的主要設計者 Roy Fielding 在他 2000 年的博士論文中提出的。
論文地址:https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm
在這篇論文中,Roy Fielding 闡述了一種 Web 軟件的架構風格(Architectural Style),並將其命名為 REST,即 Representational State Transfer(表征性狀態轉移) 的縮寫。
論文中描述了 REST 的六大軟件工程原則,符合這些原則的架構被稱為 RESTful 架構。
-
C/S架構。
-
無狀態:服務端不記錄客戶端的狀態,客戶端的每次請求中都必須包含充分的信息,以便於服務端能夠識別客戶端的狀態。
-
統一的接口
-
以資源為基礎,使用 URI 來標識資源,請求的目標對象皆為資源,每個資源都可以通過 URI 訪問到。 -
通過資源的表現層來操作資源。資源的表現層即資源的某種表示形式,比如數據庫中的一條記錄, 它的表現層可以是一段 JSON 數據,也可以是一段 HTML 數據,資源的表現層並不代表資源本身。當客戶端請求 GET 一個資源時,服務端會將該資源的表現層返回給客戶端。
-
-
可緩存:客戶端允許緩存服務端響應的內容。
-
系統分層:在終端服務器與客戶端之間允許存在中間層(如代理服務器)。
-
按需編碼(可選):服務端可以通過給客戶端返回一段功能代碼(如 Javascript 代碼)讓客戶端來執行,從而實現某些特定的功能。
作為 HTTP 協議的主要設計者,Roy Fielding 提出的 REST 架構風格恰恰是對 HTTP 協議的提倡與使用指導。HTTP 協議本身是一種面向資源的應用層協議,但是開發者們對 HTTP 的使用方式並不統一,很多 Web 服務的開發者們都並沒有完全把 HTTP 當作應用層協議,而只是把它當做傳輸層協議來用,然后在 HTTP 之上又建立起了自己的應用層協議。這是 Roy Fielding 不希望看到的。
他倡導開發者們充分利用 HTTP 協議的特性(例如使用 HTTP Method 來指定對目標資源的操作)、遵從 HTTP 設計思想來開發 Web 服務。
從某種意義上來說,REST 架構風格就是遵從了 HTTP 設計思想的 Web 架構風格。
什么是 RESTful
RESTful 是 “REST” 的形容詞形式,即“符合 REST 架構風格的”。符合 REST 架構風格的架構稱為 RESTful 架構;符合 REST 架構風格的 Web 服務稱為 RESTful Web 服務;符合 REST 架構風格的 Web API 稱為 RESTful API。
Richardson 成熟度模型
Python 爬蟲庫 BeautifulSoup 的作者 Leonard Richardson 提出了一個成熟度模型,該模型把 RESTful Web 服務按照成熟度划分成 4 個層次:
-
第一個層次(Level 0)的 Web 服務只是使用了 HTTP 作為傳輸方式,不能算是 RESTful 服務。 -
第二個層次(Level 1)的 Web 服務引入了資源的概念,使用 URI 來標識資源。 -
第三個層次(Level 2)的 Web 服務使用不同的 HTTP 方法來進行不同的操作,並且使用 HTTP 狀態碼來表示不同的操作結果。 -
第四個層次(Level 3)的 Web 服務使用 HATEOAS(超媒體作為應用程序狀態的引擎),HATEOAS 意味着資源的表現層為超媒體,超媒體是指包含指向其他資源鏈接的媒體,例如包含鏈接字段的 JSON 文本等。當客戶端訪問某個資源時,服務端給客戶端返回的數據中,除了包含目標資源自身的數據之外, 還包含與之相關的其他資源的鏈接,客戶端可以根據這些鏈接來發現其他資源。
RESTful API 設計最佳實踐
-
URI 中盡量使用名詞,原則上不使用動詞,即 URI 僅用作對資源的標識。
-
通過 HTTP Method 來指定對資源的操作。如 GET 表示獲取資源、POST 表示新建資源、PUT 表示全局更新資源(需要在請求體中包含完整的目標資源的表現層,因而不推薦使用),PATCH 表示局部更新資源,DELETE 表示刪除資源。
-
URI 中的名詞一般采用復數形式,表示某類資源的集合,如
/tickets
表示全部 ticket 的集合。 -
如果要表示集合中的單個資源,就在后面拼接這個資源的ID。如
/tickets/12
,表示 ID 為12的那個 ticket。 -
使用QueryString篩選集合中的元素,如
/tickets?status=1&sum>=100
,表示狀態為1且金額>=100的 ticket 的集合。 -
通過 URI 的層層遞進來建立資源的父子關系,如
/tickets/12/collections/3
,表示 ID 為12的那個 ticket 下的 ID 為3的 collection。 -
使用形容詞來定制對某類資源的查詢結果,如
/tickets/recently_closed
表示最近關閉的 ticket 的集合,/tickets/a_specialized
表示專門給a定制的 ticket 的集合。 -
為了支持復雜查詢,建議提供
/queries
,當客戶端需要傳遞的參數過多時,允許客戶端POST /queries
,將查詢參數放在請求體中傳遞過去,/queries
服務負責將請求體映射成一個 query 實體並寫入數據庫,然后返回 query_id。客戶端拿到 query_id 后再GET /tickets?query_id=111
。 -
關於分頁,HTTP 推薦將分頁信息放在
Link
響應頭中,參考 GitHub API 的設計,如下:Link: <https://api.github.com/user/repos?page=3&per_page=100>; rel="next",<https://api.github.com/user/repos?page=2&per_page=100>; rel="pre",<https://api.github.com/user/repos?page=1&per_page=100>; rel="first",<https://api.github.com/user/repos?page=50&per_page=100>; rel="last",
補充:HTTP 狀態碼及說明
-
101 Switching Protocols:表示需要切換網絡協議,此時客戶端應當斷開 HTTP 連接,使用指定的協議重新與服務端建立連接。
-
200 OK:表示一切正常。
-
201 Created :表示資源已成功創建,新資源的 URL 位於
Location
響應頭中,用戶可以選擇在需要的時候訪問它。 -
301 Moved Permanently:表示目標資源被永久轉移,新的 URL 位於
Location
響應頭中,此時客戶端應當對新的 URL 發起請求。 -
302 Found:表示目標資源被臨時轉移,新的 URL 位於
Location
響應頭中,此時客戶端應當對新的 URL 發起請求。 -
303 See Other:表示請求已被處理,但未返回處理結果,此時客戶端應當請求另一個資源來獲取處理結果,該資源的 URL 位於
Location
響應頭中。 -
307 Temporary Redirect:表示請求尚未被處理,是因為請求的資源不在本地,而在另一個 URL 處,客戶端應當對那個 URL 發起請求,該 URL 位於
Location
響應頭中。307 與 303 的區別:對於 GET 請求來說,307 與 303 沒有區別,對於 POST、PUT、DELETE 請求來說,它們的區別在於,307 說明請求的操作尚未執行,而 303 說明請求的操作已經執行過了。
-
400 Bad Request:表示用戶發起的請求有問題,服務端無法處理該請求。
-
401 Unauthorized:表示用戶對該資源的訪問尚未得到授權。
-
403 Forbidden:表示用戶無權訪問該資源。
-
404 Not Found:表示目標資源不存在。
-
415 Unsupported Media Type:表示請求體的媒體類型與服務端所期望的不符。
-
429 Too Many Requests:表示用戶請求的次數過多,超出了服務端的限速閾值。
-
500 Internal Server Error:表示服務端內部出現異常。
-
502 Bad Gateway:表示代理服務出現異常。
-
503 Service Unavailable:表示服務端因繁忙或故障而拒絕本次服務,並通過響應頭
Retry-After
告知客戶端何時可以重試。