那些年,咱們一塊兒誤解過的REST

歡迎你們前往騰訊雲+社區,獲取更多騰訊海量技術實踐乾貨哦~html

本文由 sammyshen 發表於 雲+社區專欄

最近幾年REST API愈來愈流行,特別是隨着微服務的概念被普遍接受和應用,不少Web Service都使用了REST API。編程

REST是HTTP規範主要編寫者之一的Roy Fielding提出的,全稱是Representational State Transfer,中文能夠翻譯爲表述性狀態轉移。它不是一種架構,而是一種架構風格。REST提出了一組架構約束條件和原則,任何知足REST約束條件和原則的架構,都稱爲RESTful架構。json

REST雖然流行,可是從業界應用的效果看,參差不齊。不少系統只是號稱是REST API,實際上並無知足REST的架構約束條件。這些系統按照本身的理解,採用了相似REST API的部分形式(如用GET/POST/PUT/DELETE進行CURD),但更多的是隨意設計,搞出了REST-RPC式,甚至是RPC式的API。這樣的API,不只沒體現出REST API的優點,反而搞成「四不像」,增長了開發維護成本。瀏覽器

如何理解REST

要規範使用RESTful架構,首先要理解什麼是REST。咱們能夠經過分別理解「表述」「狀態轉移」來理解REST。緩存

1) 表述

表述指的是資源的表示。RESTful架構是基於資源的架構(ROA, Resource-Oriented Architecture),在ROA中,處理的對象都是資源。任何須要被引用的對象,都是資源。資源表現爲某個具體的URI。服務器

所謂表述,指的是資源的某種形式的表示,這個表示不必定是全部信息,能夠只是關注的部分信息。而且,同一個資源,能夠有多個表述。例如,對於一個景點,能夠用jpeg照片來表示,也能夠用包含位置、介紹等信息的json或xml格式來分別表示。網絡

在REST中,客戶端與服務器之間的通訊,傳輸的都是資源的表述。架構

2) 狀態轉移

狀態其實應該分爲應用狀態和資源狀態。app

應用狀態由客戶端保存維護,例如會話狀態等。客戶端經過REST API返回的表述,以及表述中的URI,進行客戶端應用狀態的轉移。機器學習

但REST更強調的是資源狀態。資源狀態存儲在服務器端,客戶端經過REST API,指定請求方法、資源路徑和資源表述(能夠包含應用狀態),對資源的狀態進行增刪查改。經過增刪查改,引發資源狀態的改變,稱爲狀態轉移。

3) 結論

結合上面兩點,客戶端經過REST API對服務器端的資源進行增刪查改,引發資源的狀態轉移。而這種轉移是體如今表述上的,因此稱爲表述性狀態轉移。

怎樣纔算是符合REST架構風格

Roy Fielding在他的論文裏經過對一個空架構不斷追加約束條件,從而推導出了REST架構風格。所以,要想符合REST架構風格,則須要知足對應的約束條件。

imgimage.png

對推導過程感興趣的朋友能夠參考Roy Fielding的論文

REST的約束條件有:

  1. 統一接口
  2. 無狀態
  3. 緩存
  4. 客戶端-服務器
  5. 分層系統
  6. 按需代碼(可選)

其中,統一接口是最直觀、也是應用中誤差最大的地方,下面會重點講解。其他各約束條件則簡單講解。

1. 統一接口

統一接口其實體如今多個方面:

  • 資源URI
  • 請求參數
  • 請求方法
  • 返回碼
  • 返回內容
  • ……

1) 資源URI

RESTful架構是基於資源的架構,所操做的一切對象都是資源。所以,須要明確地定位一個資源,而URI技術正好知足這個需求,因此REST中經過URI來定位資源。

資源是一個對象,因此URI中通常只能包含名詞(通常是複數),不該該包含動詞。當須要定位具體的資源時,URI中通常包含資源的惟一ID。例如:

// 知足REST架構風格的URI
http://www.example.com/books    // 全部書籍的資源集合
http://www.example.com/books/123    // ID爲123的書籍資源

// 不知足REST架構風格的URI
http://www.example.com/books/query
http://www.example.com/buy

2) 請求參數

由於REST須要經過URI來惟必定位某個(或某種)資源,因此查詢資源時,各類資源ID通常是放在URI裏面,而不是放在請求參數裏面。請求參數中通常放過濾條件分頁信息等字段。例如:

// 知足REST架構風格的URI
http://www.example.com/books/123    // ID爲123的書籍資源
http://www.example.com/Fielding/books?page=1&per_page=10    // 做者爲Fielding的前10本書籍資源集合

// 不知足REST架構風格的URI
http://www.example.com/books?id=123
http://www.example.com/books?author=Fielding

3) 請求方法

REST約定用GET/POST/PUT/DELETE等請求方法來進行CURD操做。可是否使用了GET/POST/PUT/DELETE,並不能做爲評判一個系統是否符合REST架構風格的標準。例如,有些系統全部接口都使用GET和POST方法,若是該系統只提供查詢和建立操做,那麼多是符合REST架構風格的;但若是該系統還提供修改、刪除操做,則該系統不符合REST架構風格。

有些人認爲GET/POST/PUT/DELETE跟CURD是一對一的關係,其實不是。

具體的說,各請求方法以下:

  • GET:用於查詢資源。
  • POST:用於建立資源。POST方法建立資源的URI由服務器決定,如:POST http://www.example.com/Fieldi...,則是在ID爲123的book資源下建立一個某類別資源,如書的評論等,評論的URI也會包含一個服務器生成的ID。
  • PUT:用於建立或修改資源。PUT方法建立資源的URI由客戶端決定,如:PUT http://www.example.com/Fieldi...,當ID爲123的book資源存在時,將進行修改操做;不然進行建立操做。
  • DELETE:用於刪除資源。

另外,還有其餘較少用的請求方法,須要注意的是可能部分瀏覽器不支持。

  • HEAD:用於獲取資源的元信息。HEAD方法與GET方法相似,均可以查詢資源的元信息(放在HTTP Response的Header),但不會返回資源的表述。例如用於判斷資源是否存在。
  • PATCH:用於修改資源。與PUT方法不一樣的是,PATCH方法只傳輸改動的部分資源表述,而PUT方法須要傳輸完整的資源表述。

4) 返回碼

REST使用HTTP返回碼來表示請求的結果。若是使用規範的REST API,那麼根據HTTP返回碼就能肯定不少信息。常見的HTTP返回碼以下:

  • 200(OK):表示請求成功。
  • 201(Created):表示資源建立成功。
  • 204(No content):表示資源爲空。
  • 301(Moved Permanently):表示資源的URI已永久性更改,須要在響應內容中獲取新的URI。
  • 302(Moved Temporarily):表示資源的URI已臨時性更改,須要在響應內容中獲取新的URI。
  • 400(Bad Request):表示請求有問題,如參數錯誤等。
  • 403(Forbidden):表示鑑權不經過,沒有權限訪問該資源。
  • 404(Not Found):表示資源不存在。
  • 405(Method Not Allowed):表示該資源不支持當前的請求方法。
  • 409(Conflict):表示當前請求的某前置條件不符合。
  • 500(Internal Server Error):通用內部錯誤。
  • 502(Bad Gateway):網關錯誤,從上游服務器收到無效響應。
  • 504(Gateway Timeout):網關超時,在預期時間內沒有收到上游服務器的響應。
  • ……

還有其餘HTTP返回碼,能夠參考HTTP標準。

只要使用了規範的REST架構風格,那麼就能夠根據HTTP的標準,作出明確的相應處理,無需另外製定私有協議了。既減小了私有協議的兼容性問題,又能做爲標準適用於全部的RESTful架構。

5) 返回內容

REST API的返回內容應該是資源的表述。

前面說過,同一個資源能夠有多種不一樣格式的表述,如json格式和xml格式,因此返回內容應該是自描述的。也就是說,在HTTP響應的Header中,必須包含Content-type屬性,如application/json、application/xml、text/html等。

另外,REST是「可編程」的Web服務,也就是說,程序能夠根據REST API的返回內容,進行下一步的操做。例如,查詢author資源,下一步多是要查詢該做者著做的book資源。因此,若是author資源的表述中包含了該做者著做book資源的URI,則客戶端能夠進行相應的操做。又如,查詢某個地圖資源,地圖資源的表述中若是包含了各方向的相鄰地圖資源,則當客戶端的鼠標移到屏幕邊緣時,就能夠獲取到該方向上的地圖資源了;或者地圖資源的表述中包含景點、餐館等資源URI,則能夠進行相應的操做。

在表述中包含其餘資源的URI實現了連通性。連通性能夠做爲客戶端應用狀態的狀態引擎,引導客戶端進行下一步的操做,帶來了極大的便利。

6) 其餘

統一接口還有其餘方面的原則,本文就不細講了,感興趣的朋友能夠閱讀Fielding的論文。

2. 無狀態

無狀態約束條件是指兩次請求之間不存在依賴關係,每一次請求都包含完整的狀態信息。這裏指的狀態是指客戶端與服務器之間通訊交互的狀態,與資源狀態無關。

舉個有狀態的例子,爲了查工資,須要先登陸系統(第一次請求),再輸入查詢密碼(第二次請求)。若是前面兩次請求都經過了,那麼調用查詢接口則能夠查詢到工資;不然調用查詢接口則報未鑑權的錯誤。查詢工資接口的返回結果與前面兩次請求的狀態是關聯的,因此是有狀態的服務。

而無狀態的服務,則直接調用查詢工資接口,在請求中(通常在Header中)帶有鑑權信息,若鑑權經過則可查詢到工資,鑑權不經過則報錯。該請求不依賴於任何前置請求,稱爲無狀態。

REST使用無狀態約束條件,確保了請求的獨立性和簡單性,減小了不少跨請求的狀態維護成本。固然,帶來的代價是每次請求可能須要傳輸冗餘的信息。

3. 緩存

緩存約束條件主要是用於改善網絡的效率。緩存約束條件要求一個請求的響應中的數據被隱式地或顯式地標記爲可緩存的或不可緩存的。若是響應是可緩存的,那麼客戶端緩存就能夠爲之後的相同請求重用這個響應的數據,減小了網絡交互,提升了效率、可伸縮性和用戶感知的性能。

4. 客戶端-服務器

這個約束條件主要是分離用戶界面和數據存儲,一方面改善用戶界面跨平臺的可移植性,另外一方面簡化服務器組件,改善系統的可伸縮性。

5. 分層系統

分層系統架構約束條件將架構分爲若干層,劃定每一層的邊界,從而下降每一層設計的複雜度。同時,經過分層,能夠抽象底層的異構性,給上層提供統一的接口,簡化上層的邏輯。

6. 按需代碼

按需代碼約束條件是指某些場景下,客戶端不清楚資源的處理方法,經過向服務器請求相應的處理代碼來執行。這樣能夠簡化客戶端開發,容許部署後下載功能代碼來改善系統的可擴展性。可是,由於傳輸的是代買,下降了可見性,因此是REST的一個可選的架構約束條件。

問答
Java中的REST
相關閱讀
體驗Django REST framework,解讀REST架構風格
我是怎麼一步步用go找出壓測性能瓶頸
當 MySQL 鏈接池趕上事務(一):神祕的幽靈鎖
【每日課程推薦】機器學習實戰!快速入門在線廣告業務及CTR相應知識

此文已由做者受權騰訊雲+社區發佈,更多原文請點擊

搜索關注公衆號「雲加社區」,第一時間獲取技術乾貨,關注後回覆1024 送你一份技術課程大禮包!

海量技術實踐經驗,盡在雲加社區

相關文章
相關標籤/搜索