RESTful 接口規(guī)范

總方針

構建易于理解和使用的RESTful接口晒屎。

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

一般來說榛斯,可以使用以下的指導方針來進行接口的設計蔓钟。

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

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

資源與URI

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

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

URI的設計應該遵循可尋址性原則臼膏,具有自描述性硼被,需要在形式上給人以直覺的關聯(lián)。

URIHTTP動詞作用的對象讶请。它應該只有名詞祷嘶,不能包含動詞屎媳。

URI的設計應該注意:

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

資源操作

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)排序贫导、投影和分頁抛猫。

與之相反

經常使用的、復雜的查詢可以標簽化孩灯。
如:

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

可轉化為:

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

返回狀態(tài)碼

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

  • 1** 相關信息
  • 2** 操作成功
  • 3** 重定向相關
  • 4** 客戶端(導致的)錯誤
  • 5** 服務端(導致的)錯誤

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

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

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

返回內容

返回內容數(shù)據(jù)格式應該是結構化的(如:一個JSON對象)磅网。

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

  • 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)碼豐富的應用語義喻鳄,那么你就錯失了提高重用性、增強互操作性和提升松耦合性的機會确封。

這些所謂的RESTful應用必須通過響應體才能給出錯誤信息除呵,那么這個跟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è)務異常和非業(yè)務異常腥放。
業(yè)務異常的返回4**的狀態(tài)碼泛啸,非業(yè)務異常的返回500狀態(tài)碼绿语。

資源的表述

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

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

所以應該通過HTTP的內容協(xié)商,來獲取資源的表述煤傍。

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

區(qū)分格式

應該優(yōu)先使用內容協(xié)商來區(qū)分表述格式蚯姆。

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

超媒體(Hypermedia)

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

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

要達到這個目的显押,就要求在資源的表述里加上鏈接來引導客戶端链韭。

如 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"

應該多花時間來給資源的表述提供鏈接煮落,而不是專注于尋找漂亮的URI敞峭。

速率限制

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

如 GitHub api 中使用3個相關的頭信息進行說明:

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

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

無狀態(tài)

RESTful應該是無狀態(tài)通信的旋讹。
服務端不應該保存客戶端(應用)狀態(tài)。

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

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

服務端通過超媒體告訴客戶端當前(應用)狀態(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)雅腋么,但最明顯方便咕娄。

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

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

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

其他(Header)頭信息的使用

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

其他

1. 動詞的覆蓋

有些客戶端僅支持GETPOST兩種方法虾标。那么寓盗,服務器必須可以接受通過POST模擬其他方法(PUTPACTHDELETE)傀蚌。

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

2. 提供相關鏈接

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

參考

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市聊记,隨后出現(xiàn)的幾起案子撒妈,更是在濱河造成了極大的恐慌,老刑警劉巖排监,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狰右,死亡現(xiàn)場離奇詭異,居然都是意外死亡舆床,警方通過查閱死者的電腦和手機棋蚌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挨队,“玉大人谷暮,你說我怎么就攤上這事∈⒖眩” “怎么了湿弦?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長腾夯。 經常有香客問我省撑,道長,這世上最難降的妖魔是什么俯在? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任竟秫,我火速辦了婚禮,結果婚禮上跷乐,老公的妹妹穿的比我還像新娘肥败。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布馒稍。 她就那樣靜靜地躺著皿哨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪纽谒。 梳的紋絲不亂的頭發(fā)上证膨,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機與錄音鼓黔,去河邊找鬼央勒。 笑死,一個胖子當著我的面吹牛澳化,可吹牛的內容都是我干的崔步。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼缎谷,長吁一口氣:“原來是場噩夢啊……” “哼井濒!你這毒婦竟也來了?” 一聲冷哼從身側響起列林,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤瑞你,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后希痴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捏悬,經...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年润梯,在試婚紗的時候發(fā)現(xiàn)自己被綠了过牙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡纺铭,死狀恐怖寇钉,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情舶赔,我是刑警寧澤扫倡,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站竟纳,受9級特大地震影響撵溃,放射性物質發(fā)生泄漏。R本人自食惡果不足惜锥累,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一缘挑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧桶略,春花似錦语淘、人聲如沸诲宇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姑蓝。三九已至,卻和暖如春吕粗,著一層夾襖步出監(jiān)牢的瞬間纺荧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工颅筋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宙暇,地道東北人。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓垃沦,卻偏偏與公主長得像客给,于是被迫代替她去往敵國和親用押。 傳聞我的和親對象是個殘疾皇子肢簿,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內容

  • 一說到REST,我想大家的第一反應就是“啊蜻拨,就是那種前后臺通信方式池充。”但是在要求詳細講述它所提出的各個約束缎讼,以及如...
    時待吾閱讀 3,430評論 0 19
  • REST 是面向資源的血崭,這個概念非常重要卧惜,而資源是通過 URI 進行暴露。 REST API 是基于 HTTP的夹纫,...
    RolexOO閱讀 8,411評論 1 1
  • API定義規(guī)范 本規(guī)范設計基于如下使用場景: 請求頻率不是非常高:如果產品的使用周期內請求頻率非常高咽瓷,建議使用雙通...
    有涯逐無涯閱讀 2,548評論 0 6
  • 《新美學之旅·218·青春病毒》 春天帶來了??青春病毒! 迅速的傳染著所有人的生活習慣舰讹,情緒起伏的節(jié)奏變得更加嚴...
    蔡振源閱讀 100評論 0 3
  • 每天不到五小時的睡眠時間 不知道要喝多少速溶咖啡 與無數(shù)試卷相伴 每當撐不下去覺得要不就這么算了吧 回頭想想 不行...
    歲月慢慢閱讀 129評論 0 1