轉載:REST接口設計規範總結

簡介

Representational State Transfer 簡稱 REST 描述了一個架構樣式的網絡系統。REST 指的是一組架構約束條件和原則。知足這些約束條件和原則的應用程序或設計就是 RESTful。html

概念:git

  • 資源(Resources) REST是」表現層狀態轉化」,其實它省略了主語。」表現層」其實指的是」資源」的」表現層」。那麼什麼是資源呢?就是咱們日常上網訪問的一張圖片、一個文檔、一個視頻等。這些資源咱們經過URI來定位,也就是一個URI表示一個資源。github

  • 表現層(Representation)
    資源是作一個具體的實體信息,他能夠有多種的展示方式。而把實體展示出來就是表現層,例如一個txt文本信息,他能夠輸出成html、json、xml等格式,一個圖片他能夠jpg、png等方式展示,這個就是表現層的意思。
    URI肯定一個資源,可是如何肯定它的具體表現形式呢?應該在HTTP請求的頭信息中用Accept和Content-Type字段指定,這兩個字段纔是對」表現層」的描述。數據庫

  • 狀態轉化(State Transfer)訪問一個網站,就表明了客戶端和服務器的一個互動過程。在這個過程當中,確定涉及到數據和狀態的變化。而HTTP協議是無狀態的,那麼這些狀態確定保存在服務器端,因此若是客戶端想要通知服務器端改變數據和狀態的變化,確定要經過某種方式來通知它。json

URI格式規範

  • URI中儘可能使用連字符」-「代替下劃線」_」的使用
  • URI中統一使用小寫字母
  • URI中不要包含文件(腳本)的擴展名

    資源的原型

  • 文檔(Document)
文檔是資源的單一表現形式,能夠理解爲一個對象,或者數據庫中的一條記錄。在請求文檔時,
要麼返回文檔對應的數據,要麼會返回一個指向另一個資源(文檔)的連接。
如下是幾個基於文檔定義的URI例子:
https://api.example.com/users/will
https://api.example.com/posts/1
https://api.example.com/posts/1/comments/1
  • 集合(Collection)
集合能夠理解爲是資源的一個容器(目錄),咱們能夠向裏面添加資源(文檔)。例如:
https://api.example.com/users
https://api.example.com/posts
https://api.example.com/posts/1/comments
  • 倉庫(Store)
倉庫是客戶端來管理的一個資源庫,客戶端能夠向倉庫中新增資源或者刪除資源。
客戶端也能夠批量獲取到某個倉庫下的全部資源。倉庫中的資源對外的訪問不會提供單獨URI的,
客戶端在建立資源時候的URI除外。例如:
PUT /users/1234/favorites/posts/1  
上面的例子咱們能夠理解爲,咱們向一個id是1234的用戶的倉庫(收藏夾)中,
添加了一個id爲1的post資源。通俗點兒說:就是用戶收藏了一個本身喜好的id爲1的文章。
  • 控制器(Controller)
控制器資源模型,能夠執行一個方法,支持參數輸入,結果返回。 是爲了除了標準操做:
增刪改查(CRUD)之外的一些邏輯操做。控制器(方法)通常定義子URI中末尾,
而且不會有子資源(控制器)。例如:
向用戶重發ID爲245743的消息
POST /alerts/245743/resend  

發佈ID爲1的文章
POST /posts/1/publish

把動做轉換成資源api

把動做轉換成能夠執行 CRUD 操做的資源, github 就是用了這種方法。

好比「喜歡」一個 gist,就增長一個 /gists/:id/star 子資源,
而後對其進行操做:「喜歡」使用 PUT /gists/:id/star,
「取消喜歡」使用 DELETE /gists/:id/star
或者使用 POST /gists/:id/unstar

另一個例子是 Fork,這也是一個動做,可是在 gist 下面增長 forks資源,
就能把動做變成 CRUD 兼容的:POST /gists/:id/forks 能夠執行用戶 fork 的動做。

URI命名規範

  • 文檔(Document)類型的資源用名詞(短語)單數命名
  • 集合(Collection)類型的資源用名詞(短語)複數命名
  • 倉庫(Store)類型的資源用名詞(短語)複數命名
  • 控制器(Controller)類型的資源用動詞(短語)命名
  • URI中有些字段能夠是變量,在實際使用中能夠按需替換
例如一個資源URI能夠這樣定義:
https://api.example.com/posts/{postId}/comments/{commentId}
postId,commentId 是變量(數字,字符串都類型均可以)。
  • CRUD的操做不要體如今URI中,HTTP協議中的操做符已經對CRUD作了映射。
CRUD是建立,讀取,更新,刪除這四個經典操做的簡稱  
例如刪除的操做用REST規範執行的話,應該是這個樣子:
DELETE /users/1234

如下是幾個錯誤的示例:
GET /deleteUser?id=1234  
GET /deleteUser/1234  
DELETE /deleteUser/1234  
POST /users/1234/delete

URI的query字段

在REST中,query字段通常做爲查詢的參數補充,也能夠幫助標示一個惟一的資源。但須要注意的是,
做爲一個提供查詢功能的URI,不管是否有query條件,咱們都應該保證結果的惟一性,
一個URI對應的返回數據是不該該被改變的(在資源沒有修改的狀況下)。
HTTP中的緩存也可能緩存查詢結果。緩存

  • Query參數能夠做爲Collection或Store類型資源的過濾條件來使用 例如:
GET /users //返回全部用戶列表  
GET /users?role=admin //返回權限爲admin的用戶列表
GET /search/users?q={query}{&page,per_page,sort,order} //根據多條件查詢用戶
  • Query參數能夠做爲Collection或Store資源列表分頁標示使用
若是是一個簡單的列表操做,能夠這樣設計:
GET /users?pageSize=25&pageStartIndex=50  
若是是一個複雜的列表或查詢操做的話,咱們能夠爲資源設計一個Collection,
由於複雜查詢可能會涉及比較多的參數,建議使用Post的方式傳入,例如這樣:
POST /users/search

相關的分頁信息還能夠存放到 Link 頭部,這樣客戶端能夠直接獲得諸以下一頁、最後一頁、上一頁
等內容的 url 地址
Status: 200 OK
Link: <https://api.github.com/resource?page=2>; rel="previous",
      <https://api.github.com/resource?page=2>; rel="next",
      <https://api.github.com/resource?page=5>; rel="last"
X-RateLimit-Limit: 20
X-RateLimit-Remaining: 19

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請求
假設有兩個客戶端client#1/#2都向一個Store資源提交PUT請求,服務端是沒法清楚的判斷是要
insert仍是要update的,因此咱們要在header中加入條件標示if-Match,If-Unmodified-Since
來明確是本次調用API的意圖。例如:

client#1第一次向服務端發起一個請求 PUT /objects/2113 此時2113資源還不存在,那服務端會
認爲本次請求是一個insert操做,完成後,會返回 201 (「Created」)

client#2再一次向服務端發起同一個請求 PUT /objects/2113 時,因2113資源已存在,服務端會
返回 409 (「Conflict」)

爲了能讓client#2的請求成功,或者說咱們要清楚的代表本次操做是一次update操做,咱們必須在
header中加入一些條件標示,例如 if-Match。咱們須要給出資源的ETag(if-Match:Etag),來表
明咱們但願更新資源的版本,若是服務端版本一致,會返回200 (「OK」) 或者 204 (「No Content」)。
若是服務端發現指定的版本與當前資源版本不一致,會返回 412 (「Precondition Failed」)
  • 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 設計爲子域名,
好比 api.github.com/v3;也能夠簡單地把版… example.com/api/v1。
另外一種作法是,將版本號放在HTTP頭信息中。服務器

限流 rate limit

若是對訪問的次數不加控制,極可能會形成 API 被濫用,甚至被 DDos 攻擊。根據使用者不一樣的身份對其進行限流,能夠防止這些狀況,減小服務器的壓力。restful

對用戶的請求限流以後,要有方法告訴用戶它的請求使用狀況,Github API 使用的三個相關的頭部:網絡

  • X-RateLimit-Limit: 用戶每一個小時容許發送請求的最大值
  • X-RateLimit-Remaining:當前時間窗口剩下的可用請求數目
  • X-RateLimit-Rest: 時間窗口重置的時候,到這個時間點可用的請求數量就會變成 X-RateLimit-Limit 的值

對於超過流量的請求,能夠返回 429 Too many requests 狀態碼,並附帶錯誤信息。

參考文檔

相關文章
相關標籤/搜索