簡介
Representational State Transfer 簡稱 REST 描述了一個架構樣式的網絡系統。REST 指的是
一組架構約束條件和原則。滿足這些約束條件和原則的應用程序或設計就是 RESTful。
概念:
-
資源(Resources) REST是”表現層狀態轉化”,其實它省略了主語。”表現層”其實指的是”資源”的”表現層”。
那么什么是資源呢?就是我們平常上網訪問的一張圖片、一個文檔、一個視頻等。這些資源我們通過URI來定位,也就是一個URI表示一個資源。 -
表現層(Representation)
資源是做一個具體的實體信息,他可以有多種的展現方式。而把實體展現出來就是表現層,例如一個txt文本信息,他可以輸出成html、json、xml等格式,一個圖片他可以jpg、png等方式展現,這個就是表現層的意思。
URI確定一個資源,但是如何確定它的具體表現形式呢?應該在HTTP請求的頭信息中用Accept和Content-Type字段指定,這兩個字段才是對”表現層”的描述。 -
狀態轉化(State Transfer)
訪問一個網站,就代表了客戶端和服務器的一個互動過程。在這個過程中,肯定涉及到數據和狀態的變化。而HTTP協議是無狀態的,那么這些狀態肯定保存在服務器端,所以如果客戶端想要通知服務器端改變數據和狀態的變化,肯定要通過某種方式來通知它。
URI格式規范
-
URI中盡量使用連字符”-“代替下划線”_”的使用
-
URI中統一使用小寫字母
-
URI中不要包含文件(腳本)的擴展名
資源的原型
-
文檔(Document)
1 |
文檔是資源的單一表現形式,可以理解為一個對象,或者數據庫中的一條記錄。在請求文檔時, |
-
集合(Collection)
1 |
集合可以理解為是資源的一個容器(目錄),我們可以向里面添加資源(文檔)。例如: |
-
倉庫(Store)
1 |
倉庫是客戶端來管理的一個資源庫,客戶端可以向倉庫中新增資源或者刪除資源。 |
-
控制器(Controller)
1 |
控制器資源模型,可以執行一個方法,支持參數輸入,結果返回。 是為了除了標准操作: |
把動作轉換成資源
1 |
把動作轉換成可以執行 CRUD 操作的資源, github 就是用了這種方法。 |
URI命名規范
-
文檔(Document)類型的資源用名詞(短語)單數命名
-
集合(Collection)類型的資源用名詞(短語)復數命名
-
倉庫(Store)類型的資源用名詞(短語)復數命名
-
控制器(Controller)類型的資源用動詞(短語)命名
-
URI中有些字段可以是變量,在實際使用中可以按需替換
1 |
例如一個資源URI可以這樣定義: |
-
CRUD的操作不要體現在URI中,HTTP協議中的操作符已經對CRUD做了映射。
1 |
CRUD是創建,讀取,更新,刪除這四個經典操作的簡稱 |
URI的query字段
在REST中,query字段一般作為查詢的參數補充,也可以幫助標示一個唯一的資源。但需要注意的是,
作為一個提供查詢功能的URI,無論是否有query條件,我們都應該保證結果的唯一性,
一個URI對應的返回數據是不應該被改變的(在資源沒有修改的情況下)。
HTTP中的緩存也可能緩存查詢結果。
-
Query參數可以作為Collection或Store類型資源的過濾條件來使用 例如:
1 |
GET /users //返回所有用戶列表 |
-
Query參數可以作為Collection或Store資源列表分頁標示使用
1 |
如果是一個簡單的列表操作,可以這樣設計: |
HTTP請求方法的使用
-
GET方法用來獲取資源
-
PUT方法可用來新增/更新Store類型的資源
-
PUT方法可用來更新一個資源的全部屬性,使用時傳遞所有屬性的值,即使有的值沒有改變
-
PATCH方法更新資源的部分屬性。因為 PATCH 比較新,而且規范比較復雜,所以真正實現的比較少,
一般都是用 POST 替代 -
POST方法可用來創建一個資源
-
POST方法可用來觸發執行一個Controller類型資源
-
DELETE方法用於刪除資源
HTTP響應狀態碼的使用
-
200 (“OK”) 用於一般性的成功返回
-
200 (“OK”) 不可用於請求錯誤返回
-
201 (“Created”) 資源被創建
-
202 (“Accepted”) 用於Controller控制類資源異步處理的返回,僅表示請求已經收到。
對於耗時比較久的處理,一般用異步處理來完成 -
204 (“No Content”) 此狀態可能會出現在PUT、POST、DELETE的請求中,一般表示資源存在,
但消息體中不會返回任何資源相關的狀態或信息。 -
301 (“Moved Permanently”) 資源的URI被轉移,需要使用新的URI訪問
-
302 (“Found”) 不推薦使用,此代碼在HTTP1.1協議中被303/307替代。
我們目前對302的使用和最初HTTP1.0定義的語意是有出入的,應該只有在GET/HEAD方法下,
客戶端才能根據Location執行自動跳轉,而我們目前的客戶端基本上是不會判斷原請求方法的,
無條件的執行臨時重定向 -
303 (“See Other”) 返回一個資源地址URI的引用,但不強制要求客戶端獲取該地址的狀態(訪問該地址)
-
304 (“Not Modified”) 有一些類似於204狀態,服務器端的資源與客戶端最近訪問的資源版本一致,
並無修改,不返回資源消息體。可以用來降低服務端的壓力 -
307 (“Temporary Redirect”) 目前URI不能提供當前請求的服務,臨時性重定向到另外一個URI。
在HTTP1.1中307是用來替代早期HTTP1.0中使用不當的302 -
400 (“Bad Request”) 用於客戶端一般性錯誤返回, 在其它4xx錯誤以外的錯誤,也可以使用400,
具體錯誤信息可以放在body中 -
401 (“Unauthorized”) 在訪問一個需要驗證的資源時,驗證錯誤
-
403 (“Forbidden”) 一般用於非驗證性資源訪問被禁止,例如對於某些客戶端只開放部分API的訪問權限,
而另外一些API可能無法訪問時,可以給予403狀態 -
404 (“Not Found”) 找不到URI對應的資源
-
405 (“Method Not Allowed”) HTTP的方法不支持,例如某些只讀資源,可能不支持POST/DELETE。
但405的響應header中必須聲明該URI所支持的方法 -
406 (“Not Acceptable”) 客戶端所請求的資源數據格式類型不被支持,
例如客戶端請求數據格式為application/xml,但服務器端只支持application/json -
409 (“Conflict”) 資源狀態沖突,例如客戶端嘗試刪除一個非空的Store資源
-
412 (“Precondition Failed”) 用於有條件的操作不被滿足時
-
415 (“Unsupported Media Type”) 客戶所支持的數據類型,服務端無法滿足
-
429 (“Too Many Requests”) 客戶端在規定的時間里發送了太多請求,在進行限流的時候會用到
-
500 (“Internal Server Error”) 服務器端的接口錯誤,此錯誤於客戶端無關
HTTP Headers
-
Content-Type 標示body的數據格式
-
Content-Length body 數據體的大小,客戶端可以根據此標示檢驗讀取到的數據是否完整,
也可以通過Header判斷是否需要下載可能較大的數據體 -
Last-Modified 用於服務器端的響應,是一個資源最后被修改的時間戳,客戶端(緩存)可以根據
此信息判斷是否需要重新獲取該資源 -
ETag 服務器端資源版本的標示,客戶端(緩存)可以根據此信息判斷是否需要重新獲取該資源,
需要注意的是,ETag如果通過服務器隨機生成,可能會存在多個主機對同一個資源產生不同ETag的問題 -
Store類型的資源要支持有條件的PUT請求
1 |
假設有兩個客戶端client#1/#2都向一個Store資源提交PUT請求,服務端是無法清楚的判斷是要 |
-
Location 在響應header中使用,一般為客戶端感興趣的資源URI,例如在成功創建一個資源后,我們
可以把新的資源URI放在Location中,如果是一個異步創建資源的請求,接口在響應202 (“Accepted”)
的同時可以給予客戶端一個異步狀態查詢的地址 -
Cache-Control, Expires, Date 通過緩存機制提升接口響應性能,同時根據實際需要也可以禁止
客戶端對接口請求做緩存。對於REST接口來說,如果某些接口實時性要求不高的情況下,我們可以使
用max-age來指定一個小的緩存時間,這樣對客戶端和服務器端雙方都是有利的。一般來說只對GET
方法且返回200的情況下使用緩存,在某些情況下我們也可以對返回3xx或者4xx的情況下做緩存,可
以防范錯誤訪問帶來的負載。 -
我們可以自定義一些頭信息,作為客戶端和服務器間的通信使用,但不能改變HTTP方法的性質。自
定義頭盡量簡單明了,不要用body中的信息對其作補充說明。
API 地址和版本
在 url 中指定 API 的版本是個很好地做法。如果 API 變化比較大,可以把 API 設計為子域名,
比如 https://api.github.com/v3;也可以簡單地把版本放在路徑中,比如 https://example.com/api/v1。
另一種做法是,將版本號放在HTTP頭信息中。
限流 rate limit
如果對訪問的次數不加控制,很可能會造成 API 被濫用,甚至被 DDos 攻擊。根據使用者不同的身
份對其進行限流,可以防止這些情況,減少服務器的壓力。
對用戶的請求限流之后,要有方法告訴用戶它的請求使用情況,Github API 使用的三個相關的頭部:
-
X-RateLimit-Limit: 用戶每個小時允許發送請求的最大值
-
X-RateLimit-Remaining:當前時間窗口剩下的可用請求數目
-
X-RateLimit-Rest: 時間窗口重置的時候,到這個時間點可用的請求數量就會變成 X-RateLimit-Limit 的值
對於超過流量的請求,可以返回 429 Too many requests 狀態碼,並附帶錯誤信息。
參考文檔
-
http://cizixs.com/2016/12/12/restful-api-design-guide
-
http://wangwei.info/about-rest-api/
-
http://www.ruanyifeng.com/blog/2011/09/restful.html
-
http://www.ruanyifeng.com/blog/2014/05/restful_api.html
-
https://zh.wikipedia.org/wiki/REST
-
https://developer.github.com/v3