關於RESTful API 設計的總結

爲何要用 RESTful

RESTful 給個人最大感受就是規範、易懂和優雅,一個結構清晰、易於理解的 API 徹底能夠省去許多無心義的溝通和文檔。而且 RESTful 如今愈來愈流行,html

在開始介紹 RESTful API 以前,先介紹一下 RESTful 架構。api

RESTful 架構

REST,即Representational State Transfer 的縮寫。意爲 " 表現層狀態轉化 " 。緩存

要理解RESTful架構,最好的方法就是去理解 Representational State Transfer 這個詞組究竟是什麼意思,它的每個詞表明瞭什麼涵義。若是把這個名稱搞懂了,也就不難體會 REST 是一種什麼樣的設計。服務器

資源 (Resources)

REST的名稱"表現層狀態轉化"中,省略了主語。"表現層"其實指的是"資源"(Resources)的"表現層"。網絡

所謂"資源",就是網絡上的一個實體,或者說是網絡上的一個具體信息。它能夠是一段文本、一張圖片、一首歌曲、一種服務,總之就是一個具體的實在。你能夠用一個 URI(統一資源定位符)指向它,每種資源對應一個特定的 URI 。要獲取這個資源,訪問它的 URI 就能夠,所以 URI 就成了每個資源的地址或獨一無二的識別符。所謂"上網",就是與互聯網上一系列的"資源"互動,調用它的 URI 。架構

表現層(Representation)

"資源"是一種信息實體,它能夠有多種外在表現形式。咱們把"資源"具體呈現出來的形式,叫作它的"表現層"(Representation)。
好比,文本能夠用 txt 格式表現,也能夠用 HTML 格式、 XML 格式、JSON 格式表現,甚至能夠採用二進制格式;圖片能夠用 JPG 格式表現,也能夠用 PNG 格式表現。
URI 只表明資源的實體,不表明它的形式。嚴格地說,有些網址最後的" .html "後綴名是沒必要要的,由於這個後綴名錶示格式,屬於"表現層"範疇,而 URI 應該只表明"資源"的位置。它的具體表現形式,應該在 HTTP 請求的頭信息中用 Accept 和 Content-Type 字段指定,這兩個字段纔是對"表現層"的描述。性能

狀態轉化(State Transfer)

訪問一個網站,就表明了客戶端和服務器的一個互動過程。在這個過程當中,勢必涉及到數據和狀態的變化。
互聯網通訊協議 HTTP 協議,是一個無狀態協議。這意味着,全部的狀態都保存在服務器端。所以,若是客戶端想要操做服務器,必須經過某種手段,讓服務器端發生"狀態轉化"(State Transfer)。而這種轉化是創建在表現層之上的,因此就是"表現層狀態轉化"。
客戶端用到的手段,只能是HTTP協議。具體來講,就是HTTP協議裏面,四個表示操做方式的動詞:GET 、 POST 、 PUT 、 DELETE 。 它們分別對應四種基本操做: GET 用來獲取資源, POST 用來新建資源,PUT 用來更新資源,DELETE 用來刪除資源。網站

綜述

總結一下什麼是RESTful架構:加密

  • 每個URI表明一種資源;
  • 客戶端和服務器之間,傳遞這種資源的某種表現層;
  • 客戶端經過四個HTTP動詞,對服務器端資源進行操做,實現"表現層狀態轉化"。

RESTful API 的設計

介紹完 RESTful 的含義,再說說 RESTful API 的設計。設計

協議

若是能全站 HTTPS 固然是最好的,不能的話也請儘可能將登陸、註冊等涉及密碼的接口使用 HTTPS。

域名

應該儘可能將API部署在專用域名之下。如:

https://api.example.com

若是肯定API很簡單,不會有進一步擴展,能夠考慮放在主域名下。

https://example.org/api/

版本號

API 的版本號和客戶端 APP 的版本號是毫無關係的,不要讓 APP 將它們用於提交應用市場的版本號傳遞到服務器,而是提供相似於v一、v2之類的 API 版本號。

版本號拼接在 URL 中。如:

api.example.com/v1/users

或是放在 Header 中:

api.xxx.com/users

version=v1

請求

通常來講 API 的外在形式無非就是增刪改查(固然具體的業務邏輯確定要複雜得多),而查詢又分爲詳情和列表兩種,在 RESTful 中這就至關於通用的模板。例如針對文章(Article)設計 API,那麼最基礎的 URL 就是這幾種:

  • GET /articles: 文章列表
  • GET /articles/id:文章詳情
  • POST /articles/: 建立文章
  • PUT /articles/id:修改文章
  • DELETE /articles/id:刪除文章

Token 和 Sign

API 須要設計成無狀態,因此客戶端在每次請求時都須要提供有效的 Token 和 Sign,在我看來它們的用途分別是:

  • Token 用於證實請求所屬的用戶,通常都是服務端在登陸後隨機生成一段字符串(UUID)和登陸用戶進行綁定,再將其返回給客戶端。Token 的狀態保持通常有兩種方式實現:一種是在用戶每次操做都會延長或重置 TOKEN 的生存時間(相似於緩存的機制),另外一種是 Token 的生存時間固定不變,可是同時返回一個刷新用的 Token,當 Token 過時時能夠將其刷新而不是從新登陸。
  • Sign 用於證實該次請求合理,因此通常客戶端會把請求參數拼接後並加密做爲 Sign 傳給服務端,這樣即便被抓包了,對方只修改參數而沒法生成對應的 Sign 也會被服務端識破。固然也能夠將時間戳、請求地址和 Token 也混入 Sign,這樣 Sign 也擁有了所屬人、時效性和目的地。

業務參數

在 RESTful 的標準中,PUT 和 PATCH 均可以用於修改操做,它們的區別是 PUT 須要提交整個對象,而 PATCH 只須要提交修改的信息。可是在我看來實際應用中不須要這麼麻煩,因此我一概使用 PUT,而且只提交修改的信息。

另外一個問題是在 POST 建立對象時,究竟該用表單提交更好些仍是用 JSON 提交更好些。其實二者均可以,在我看來它們惟一的區別是 JSON 能夠比較方便的表示更爲複雜的結構(有嵌套對象)。另外不管使用哪一種,請保持統一,不要二者混用。

還有一個建議是最好將過濾、分頁和排序的相關信息全權交給客戶端,包括過濾條件、頁數或是遊標、每頁的數量、排序方式、升降序等,這樣可使 API 更加靈活。可是對於過濾條件、排序方式等,不須要支持全部方式,只須要支持目前用得上的和之後可能會用上的方式便可,並經過字符串枚舉解析,這樣可見性要更好些。例如:

搜索,客戶端只提供關鍵詞,具體搜索的字段,和搜索方式(前綴、全文、精確)由服務端決定:

/users/?query=ScienJus

過濾,只須要對已有的狀況進行支持:

/users/?gender=1

分頁:

/users/?page=2&pre_page=20

響應

儘可能使用 HTTP 狀態碼,經常使用的有:

  • 200:請求成功
  • 201:建立、修改爲功
  • 204:刪除成功
  • 400:參數錯誤
  • 401:未登陸
  • 403:禁止訪問
  • 404:未找到
  • 500:系統錯誤

可是有些時候僅僅使用 HTTP 狀態碼沒有辦法明確的表達錯誤信息,因此也能夠在裏面再包一層自定義的返回碼,例如:

成功時:

{
    "code": 100,
    "msg": "成功",
    "data": {}
}
{
    "code": -1000,
    "msg": "用戶名或密碼錯誤"
}

data是真正須要返回的數據,而且只會在請求成功時才存在,msg只用在開發環境,而且只爲了開發人員識別。客戶端邏輯只容許識別code,而且不容許直接將msg的內容展現給用戶。若是這個錯誤很複雜,沒法使用一段話描述清楚,也能夠在添加一個doc字段,包含指向該錯誤的文檔的連接。

返回數據

JSON 比 XML 可視化更好,也更加節約流量,因此儘可能不要使用 XML。

建立和修改操做成功後,須要返回該資源的所有信息。

返回數據不要和客戶端界面強耦合,不要在設計 API 時就考慮少查詢一張關聯表或是少查詢 / 返回幾個字段能帶來多大的性能提高。而且必定要以資源爲單位,即便客戶端一個頁面須要展現多個資源,也不要在一個接口中所有返回,而是讓客戶端分別請求多個接口。

最好將返回數據進行加密和壓縮,尤爲是壓縮在移動應用中仍是比較重要的。

相關文章
相關標籤/搜索