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例子: https://api.example.com/users/will https://api.example.com/posts/1 https://api.example.com/posts/1/comments/1
集合能夠理解爲是資源的一個容器(目錄),咱們能夠向裏面添加資源(文檔)。例如: https://api.example.com/users https://api.example.com/posts https://api.example.com/posts/1/comments
倉庫是客戶端來管理的一個資源庫,客戶端能夠向倉庫中新增資源或者刪除資源。 客戶端也能夠批量獲取到某個倉庫下的全部資源。倉庫中的資源對外的訪問不會提供單獨URI的, 客戶端在建立資源時候的URI除外。例如: PUT /users/1234/favorites/posts/1 上面的例子咱們能夠理解爲,咱們向一個id是1234的用戶的倉庫(收藏夾)中, 添加了一個id爲1的post資源。通俗點兒說:就是用戶收藏了一個本身喜好的id爲1的文章。
控制器資源模型,能夠執行一個方法,支持參數輸入,結果返回。 是爲了除了標準操做: 增刪改查(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能夠這樣定義: https://api.example.com/posts/{postId}/comments/{commentId} postId,commentId 是變量(數字,字符串都類型均可以)。
CRUD是建立,讀取,更新,刪除這四個經典操做的簡稱 例如刪除的操做用REST規範執行的話,應該是這個樣子: DELETE /users/1234 如下是幾個錯誤的示例: GET /deleteUser?id=1234 GET /deleteUser/1234 DELETE /deleteUser/1234 POST /users/1234/delete
在REST中,query字段通常做爲查詢的參數補充,也能夠幫助標示一個惟一的資源。但須要注意的是,
做爲一個提供查詢功能的URI,不管是否有query條件,咱們都應該保證結果的惟一性,
一個URI對應的返回數據是不該該被改變的(在資源沒有修改的狀況下)。
HTTP中的緩存也可能緩存查詢結果。緩存
GET /users //返回全部用戶列表 GET /users?role=admin //返回權限爲admin的用戶列表 GET /search/users?q={query}{&page,per_page,sort,order} //根據多條件查詢用戶
若是是一個簡單的列表操做,能夠這樣設計: 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
假設有兩個客戶端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」)
在 url 中指定 API 的版本是個很好地作法。若是 API 變化比較大,能夠把 API 設計爲子域名,
好比 api.github.com/v3;也能夠簡單地把版… example.com/api/v1。
另外一種作法是,將版本號放在HTTP頭信息中。服務器
若是對訪問的次數不加控制,極可能會形成 API 被濫用,甚至被 DDos 攻擊。根據使用者不一樣的身份對其進行限流,能夠防止這些狀況,減小服務器的壓力。restful
對用戶的請求限流以後,要有方法告訴用戶它的請求使用狀況,Github API 使用的三個相關的頭部:網絡
對於超過流量的請求,能夠返回 429 Too many requests 狀態碼,並附帶錯誤信息。