RESTful 接口規(guī)范

總方針

構(gòu)建易于理解和使用的RESTful接口。

接口應(yīng)該是直觀的,調(diào)用者可以通過接口來獲得系統(tǒng)或應(yīng)用程序中所有業(yè)務(wù)服務(wù)的工作節(jié)奏。

一般來說,可以使用以下的指導(dǎo)方針來進行接口的設(shè)計。

  1. 使用標準HTTP動詞--圍繞這些HTTP動詞(GET/PUT/POST/PATCHDELETE)對基本的行為進行建模。
  2. 使用URI來傳達意圖--使用URI來描述問題域中的不同資源,并為問題域內(nèi)的資源的關(guān)系提供一種基本機制。
  3. 使用JSON進行響應(yīng)--JSON是一種輕量級的數(shù)據(jù)序列化協(xié)議。
  4. 使用HTTP狀態(tài)碼來傳達結(jié)果--HTTP協(xié)議具有豐富的標準響應(yīng)代碼,來指示服務(wù)的成功和失敗。學(xué)習這些狀態(tài)碼,并且,最重要的是,在所有接口中始終如一地使用它們。

所有這些指導(dǎo)方針都是為了完成一件事,那就是使接口易于理解和使用。
我們希望調(diào)用者坐下來查看一下接口就能開始使用它們。
如果接口不容易使用,開發(fā)人員就會另辟道路,破壞架構(gòu)的意圖。

資源與URI

REST全稱是 Representational State Transfer (表述性狀態(tài)轉(zhuǎn)移)。其中表述指的就是資源。

URI既可看成是資源的地址,也可以看成是資源的名稱。

URI的設(shè)計應(yīng)該遵循可尋址性原則,具有自描述性,需要在形式上給人以直覺的關(guān)聯(lián)。

URIHTTP動詞作用的對象。它應(yīng)該只有名詞,不能包含動詞。

URI的設(shè)計應(yīng)該注意:

  1. URI中不能有動詞: 動詞應(yīng)該由HTTP的動作(GET/POST/PUT/PATCH/DELETE等)來表示
  2. URI結(jié)尾不應(yīng)該包含斜杠“/”
  3. 正斜杠分隔符“/”必須用來指示層級關(guān)系
  4. 應(yīng)該使用連接符“-”來提高URI的可讀性:瀏覽器默認會給超鏈接加上下劃線,因此不要用其做URI分隔符
  5. URI路徑首選小寫字母:RFC-3986URI定義為區(qū)分大小寫,但URI中的scheme(協(xié)議名)和host(主機名)除外
  6. URI路徑中的名詞建議使用復(fù)數(shù)
  7. 避免層級過深的URI(太多的層級在另一個側(cè)面反應(yīng)該接口有太多的職責)

資源操作

HTTP 通常有以下5種動詞:

  • GET:獲取資源(冪等)
  • POST:新建資源(非冪等)
  • PUT:更新資源(所有屬性)(冪等)
  • PATCH:更新資源(部分屬性)(非冪等)
  • DELETE:刪除資源(冪等)

根據(jù) HTTP 規(guī)范,動詞一律大寫。

資源過濾

很多情況,資源會有多級分類,因此很容易寫出多級的URI,比如某個作者的某一類文章(/authors/123/categories/2)。

這種URI不易于擴展,語義也不明確,不能直觀表達其含義。

更好的做法是,將次要的級別用查詢字符串進行表達。如:

/authors/123?category=2
/articles?published=true

同樣的,通過使用查詢字符串,實現(xiàn)排序、投影和分頁。

與之相反

經(jīng)常使用的、復(fù)雜的查詢可以標簽化。
如:

/authors/123?status=close&sort=created,desc

可轉(zhuǎn)化為:

/authors/123/closed
// 或者
/authors/123#closed

返回狀態(tài)碼

HTTP狀態(tài)碼為三位數(shù),分五類:

  • 1** 相關(guān)信息
  • 2** 操作成功
  • 3** 重定向相關(guān)
  • 4** 客戶端(導(dǎo)致的)錯誤
  • 5** 服務(wù)端(導(dǎo)致的)錯誤

HTTP包含了100多個狀態(tài)碼,覆蓋了大多數(shù)可能遇到的情況。
每一種狀態(tài)都有標準的(或約定的)解釋,客戶端只需查看狀態(tài),就可以大致判斷發(fā)生了什么情況。
所以服務(wù)器應(yīng)該盡可能使用這些標準的HTTP狀態(tài)碼,來表達執(zhí)行結(jié)果狀態(tài)。

通常不需要1**這一類狀態(tài)碼。
以下是常用的:

  • 200 OK : 成功返回請求數(shù)據(jù)(冪等)
  • 201 Created : 新建數(shù)據(jù)成功
  • 202 Accepted : 表示服務(wù)器已接收請求,但未處理。通常用于異步操作。
  • 204 No Content : 刪除數(shù)據(jù)成功
  • 301 Moved Permanently : 資源已永久性遷移,需要使用新的(寫在相應(yīng)頭Location中的)URI訪問。允許客戶端把POST請求修改為GET
  • 302 Found : 不推薦使用,此代碼在HTTP1.1協(xié)議中已被303/307替代。目前對302的使用和最初的HTTP1.0定義的語義是有出入的,應(yīng)該只有在GET/HEAD方法下,客戶端才能根據(jù)Location執(zhí)行自動跳轉(zhuǎn),而目前的客戶端基本上是不會判斷原請求方法,無條件的執(zhí)行重定向。
  • 303 See Other : 參考另一個URI(區(qū)別:307用于GET;303用于POST、PUTDELETE),但不強制要求重定向。
  • 304 Not Modified : 服務(wù)器資源與客戶端最近訪問的一致,不返回資源消息體。
  • 307 Temporary Redirect : 目前URI不能提供所請求的資源,臨時重定向到另外一個URI。用來替代HTTP1.0中的302
  • 308 Permanent Redirect : 與301類似,但客戶端不能修改原請求方法
  • 400 Bad Request : 服務(wù)器不理解客戶端的請求,未做任何處理
  • 401 Unauthorized : 用戶未提供身份驗證憑據(jù),或沒通過驗證
  • 403 Forbidden : 用戶通過的驗證,但不具有訪問權(quán)限
  • 404 Not Found : 請求資源不存在或不可用??梢詫δ承┯脩粑词跈?quán)訪問的資源操作返回該狀態(tài)碼,目的是防止私有資源泄露(知道有該資源)。
  • 405 Method Not Allowed : 用戶已通過驗證,但所用的HTTP方法不在權(quán)限內(nèi)或資源只讀等。響應(yīng)Header中應(yīng)申明支持的方法
  • 406 Not Acceptable : 表示拒絕處理該請求(如:服務(wù)端只能返回JSON,但客戶端要求XML
  • 409 Conflict : 資源狀態(tài)沖突,例如客戶端嘗試刪除一個有約束的資源
  • 410 Gone : 請求資源已從這個地址轉(zhuǎn)移,不再可用
  • 412 Precondition Failed : 用于有條件的操作不能被滿足
  • 415 Unsupported Media Type : 請求格式不支持(如:服務(wù)端只能返回JSON,但客戶端要求XML
  • 422 Unprocessable Entity : 請求無法處理,或發(fā)生了一個驗證錯誤
  • 429 Too Many Requests : 請求次數(shù)超過限制
  • 500 Internal Server Error : 請求有效,服務(wù)器處理時發(fā)生內(nèi)部錯誤
  • 503 Service Unavailable : 服務(wù)器無法處理請求,多半是服務(wù)器問題,如CPU高等

返回內(nèi)容

返回內(nèi)容數(shù)據(jù)格式應(yīng)該是結(jié)構(gòu)化的(如:一個JSON對象)。

客戶端請求時也要明確告訴服務(wù)器,可以接受的格式。

  • GET /collections 200 返回資源列表
  • GET /collections/:id 200 返回單個資源
  • POST /colections 201 返回新增的資源對象
  • PUT /collections/:id 200 返回完整的資源對象
  • PATCH /collections/:id 200 返回完整的資源對象或被修改的屬性
  • DELETE /collections/:id 204 返回空文檔

錯誤處理

錯誤時不要返回200狀態(tài)碼。
因為只有解析數(shù)據(jù)體后,才能得知操作失敗。而且與HTTP狀態(tài)碼描述沖突。

假如你不利用HTTP狀態(tài)碼豐富的應(yīng)用語義,那么你就錯失了提高重用性、增強互操作性和提升松耦合性的機會。

這些所謂的RESTful應(yīng)用必須通過響應(yīng)體才能給出錯誤信息,那么這個跟SOAP沒什么區(qū)別。

正確的做法是,狀態(tài)碼反映發(fā)生的錯誤,而具體的錯誤信息放在數(shù)據(jù)體中。
如:

HTTP/1.1 400 Bab Request
Content-Type: application/json

{
    "error": "Invalid param."
    "data": {
        "name": "This field is required."
    }
}

另外建議要區(qū)分業(yè)務(wù)異常和非業(yè)務(wù)異常。
業(yè)務(wù)異常的返回4**的狀態(tài)碼,非業(yè)務(wù)異常的返回500狀態(tài)碼。

資源的表述

客戶端通過HTTP方法獲取的不是資源本身,而是資源的一種表述而已。
資源在外界的具體呈現(xiàn),可以有多種表述形式,如:html、xml、json、png、jpg等。

資源的表述包括數(shù)據(jù)和描述數(shù)據(jù)的元數(shù)據(jù),如:HTTP頭中的Content-Type就是一個元數(shù)據(jù)屬性。

所以應(yīng)該通過HTTP的內(nèi)容協(xié)商,來獲取資源的表述。

如:客戶端可以通過Accept頭請求一種特定格式的表述,服務(wù)器則通過Content-Type告訴客戶端資源的表述形式。

區(qū)分格式

應(yīng)該優(yōu)先使用內(nèi)容協(xié)商來區(qū)分表述格式。

使用后綴表示格式,無疑是直觀的,但它混淆了資源的名稱和資源的表述形式。

超媒體(Hypermedia)

“超媒體即應(yīng)用狀態(tài)引擎(hypermedia as the engine of application state)”。

當瀏覽Web網(wǎng)頁時,我們從一個鏈接跳到一個頁面,再從頁面里的另一個鏈接跳到另一個頁面,這就是在用超媒體的概念:把一個個資源鏈接起來。

要達到這個目的,就要求在資源的表述里加上鏈接來引導(dǎo)客戶端。

如 GitHub api 中的分頁,是在頭信息的Link提供:

Link: <https://api.github.com/search/code?q=addClass+user%3Amozilla&page=15>; rel="next",
  <https://api.github.com/search/code?q=addClass+user%3Amozilla&page=34>; rel="last",
  <https://api.github.com/search/code?q=addClass+user%3Amozilla&page=1>; rel="first",
  <https://api.github.com/search/code?q=addClass+user%3Amozilla&page=13>; rel="prev"

應(yīng)該多花時間來給資源的表述提供鏈接,而不是專注于尋找漂亮的URI

速率限制

響應(yīng)頭建議包含當前限流狀態(tài)

如 GitHub api 中使用3個相關(guān)的頭信息進行說明:

  • X-RateLimit-Limit: 用戶在時間窗口下發(fā)送請求的最大值
  • X-RateLimit-Remaining: 當前時間窗口剩下的可用請求數(shù)
  • X-RateLimit-Rest: 為了得到最大請求數(shù)(或到下一時間窗口)所等待的秒數(shù)

建議同時提供可以不影響RateLimit的請求接口,來查詢當前的RateLimit。

無狀態(tài)

RESTful應(yīng)該是無狀態(tài)通信的。
服務(wù)端不應(yīng)該保存客戶端(應(yīng)用)狀態(tài)。

客戶端與服務(wù)端交互必須是無狀態(tài)的,并在每次請求中包含處理所需的一切信息。

這種無狀態(tài)通信,使得服務(wù)端能夠理解獨立的請求和響應(yīng)。
在多次請求中,同一客戶端也不再需要依賴同一服務(wù)器,方便實現(xiàn)高可擴展和高可用性的服務(wù)端。

服務(wù)端通過超媒體告訴客戶端當前(應(yīng)用)狀態(tài)可以有哪些后續(xù)的狀態(tài)。
這些類似“下一頁”的鏈接將指引客戶端如何從當前狀態(tài)進入下一個可能的狀態(tài)。

版本

三種方式:

  1. URI中:/api/v1/**
  2. Accept Header: Accept: application/json+v1
  3. 自定義Header: X-Api-Version: 1

建議第一種,雖然沒那么優(yōu)雅,但最明顯方便。

另一種觀點:一個資源,應(yīng)只有一個單一的URI來標示,資源版本不應(yīng)該體現(xiàn)在URI中。

以上見仁見智,不強制要求。

  • API 失效:返回404 Not Found410 Gone
  • API 遷移:返回301/303307

其他(Header)頭信息的使用

  • Last-Modified : 用于服務(wù)器端的響應(yīng),是一個資源最后被修改的時間戳,客戶端(緩存)可以根據(jù)此信息判斷是否需要重新獲取該資源。
  • ETag : 服務(wù)器端資源版本的標識,客戶端(緩存)可以根據(jù)此信息判斷是否需要重新獲取該資源,需要注意的是,ETage如果通過服務(wù)器隨機生產(chǎn),可能會存在多個主機對同一個資源產(chǎn)生不同的ETag的問題。
  • Location : 如在成功創(chuàng)建了一個資源后,可以把新資源的URL放在Location中;又如,在異步請求時,接口返回響應(yīng)202(Accepted)的同時,可以給客戶端一個查詢異步狀態(tài)的地址。
  • Cache-Control, Expires, Date : 通過緩存機制提升接口響應(yīng)性能,同時根據(jù)實際需要也可以禁止客戶端對接口請求做緩存。對應(yīng)某些實時性要求不高的情況下,可以使用max-age來指定一個小的緩存時間,這樣對客戶端和服務(wù)端都有利。一般來說,只對GET方法且返回200的資源使用緩存,在某些情況下也可以對返回3**4**的情況做緩存,防范錯誤訪問帶來的負載。
  • 自定義頭 : 不能改變HTTP方法的性質(zhì),盡量保持的簡單,不要用body中的信息對其補充說明。

其他

1. 動詞的覆蓋

有些客戶端僅支持GETPOST兩種方法。那么,服務(wù)器必須可以接受通過POST模擬其他方法(PUT、PACTH、DELETE)。

客戶端在發(fā)送HTTP請求時,要加上X-HTTP-Method-Override頭信息,告訴服務(wù)器應(yīng)該使用那個動詞,覆蓋POST方法。

2. 提供相關(guān)鏈接

服務(wù)接口的使用者未必知道接口有那些,以及它的相關(guān)服務(wù)。
好的接口,應(yīng)該在相應(yīng)中給出相關(guān)鏈接,以便于下一步操作。
這樣,用戶就可以發(fā)現(xiàn)其他接口的URI。
這種方法叫HATEOAS。
如 GitHub 的 API 都在api.github.com這個域名。

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,197評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,415評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,104評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,884評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,647評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,130評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,208評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,366評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,887評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,737評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,478評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,174評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,586評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,827評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,608評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,914評論 2 372

推薦閱讀更多精彩內(nèi)容

  • 一說到REST,我想大家的第一反應(yīng)就是“啊,就是那種前后臺通信方式?!钡窃谝笤敿氈v述它所提出的各個約束,以及如...
    時待吾閱讀 3,444評論 0 19
  • REST 是面向資源的,這個概念非常重要,而資源是通過 URI 進行暴露。 REST API 是基于 HTTP的,...
    RolexOO閱讀 8,420評論 1 1
  • API定義規(guī)范 本規(guī)范設(shè)計基于如下使用場景: 請求頻率不是非常高:如果產(chǎn)品的使用周期內(nèi)請求頻率非常高,建議使用雙通...
    有涯逐無涯閱讀 2,575評論 0 6
  • 《新美學(xué)之旅·218·青春病毒》 春天帶來了??青春病毒! 迅速的傳染著所有人的生活習慣,情緒起伏的節(jié)奏變得更加嚴...
    蔡振源閱讀 103評論 0 3
  • 每天不到五小時的睡眠時間 不知道要喝多少速溶咖啡 與無數(shù)試卷相伴 每當撐不下去覺得要不就這么算了吧 回頭想想 不行...
    歲月慢慢閱讀 132評論 0 1