理解RESTful:理論與最佳實踐

張永恆發表於2020-09-30

什麼是 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 架構。

  1. C/S架構。

  2. 無狀態:服務端不記錄客戶端的狀態,客戶端的每次請求中都必須包含充分的資訊,以便於服務端能夠識別客戶端的狀態。

  3. 統一的介面

    1. 以資源為基礎,使用 URI 來標識資源,請求的目標物件皆為資源,每個資源都可以通過 URI 訪問到。
    2. 通過資源的表現層來操作資源。資源的表現層即資源的某種表示形式,比如資料庫中的一條記錄, 它的表現層可以是一段 JSON 資料,也可以是一段 HTML 資料,資源的表現層並不代表資源本身。當客戶端請求 GET 一個資源時,服務端會將該資源的表現層返回給客戶端。
  4. 可快取:客戶端允許快取服務端響應的內容。

  5. 系統分層:在終端伺服器與客戶端之間允許存在中間層(如代理伺服器)。

  6. 按需編碼(可選):服務端可以通過給客戶端返回一段功能程式碼(如 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 個層次:

  1. 第一個層次(Level 0)的 Web 服務只是使用了 HTTP 作為傳輸方式,不能算是 RESTful 服務。
  2. 第二個層次(Level 1)的 Web 服務引入了資源的概念,使用 URI 來標識資源。
  3. 第三個層次(Level 2)的 Web 服務使用不同的 HTTP 方法來進行不同的操作,並且使用 HTTP 狀態碼來表示不同的操作結果。
  4. 第四個層次(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告知客戶端何時可以重試。

相關文章