一種RESTful接口的約定

1 概述

1.1 撰寫目的

本文用于定義一種統(tǒng)一的RESTful接口設計方案,希望具有參考價值。本文所描述的方案比較學院派两残,在上一家公司提出沒有被采納,在所了解到的有限的若干家聲稱采用了RESTful風格的公司里委刘,發(fā)現(xiàn)他們也偏離甚遠丧没。當然,他們這么做是有理由的锡移,我也理解呕童,這只是取舍問題。這篇文章其實是舊文了淆珊,2016年年底就已經寫好夺饲,但是一直躺在電腦的硬盤里,不想白費了當時的功夫施符,因此在此公開往声。

1.2 為什么采用REST

目的是為了服務端與客戶端的解耦。SOA僅僅是從結構上將前后端分離戳吝,但是實際上數(shù)據(jù)邏輯還是沒有實現(xiàn)解耦浩销,服務端接口升級往往會影響客戶端,兩者的行為需要嚴格約定听哭。而REST采用HTTP協(xié)議進行約定慢洋,客戶端僅僅需要按照HTTP協(xié)議來理解服務端返回的數(shù)據(jù),雖然與業(yè)務相關的數(shù)據(jù)結構還是需要約定陆盘,但是這確實進一步解耦了服務端與客戶端普筹。

另外,由于嚴格遵照HTTP協(xié)議進行數(shù)據(jù)返回隘马,對于安全的接口太防,可以在返回的Header里設置緩存策略(接口安全性的概念在下文會解釋)。

1.3 文檔結構

第二部分將闡述關于RESTful的若干個關鍵的概念祟霍,明確第二部分闡述的幾個概念有利于設計杏头、實現(xiàn)優(yōu)雅規(guī)范的接口盈包。

第三部分就URL命名的問題進行約定。

第四部分對消息實體進行約定醇王。

第五部分對『向RESTful接口發(fā)起請求』進行闡述呢燥,約定要實現(xiàn)的方法,約定請求的頭部和body的格式寓娩。

第六部分對接口的響應格式進行約定叛氨,包括響應消息的頭部、狀態(tài)碼棘伴、JSON實體寞埠。

第七部分對版本控制的問題進行約定。

第八部分對RESTful接口的實現(xiàn)提出了實現(xiàn)工具的建議焊夸。

2 關鍵概念

明確一些關鍵的概念是很重要的仁连,雖然RESTful風格的API設計方案并沒有統(tǒng)一的標準,但是還是需要符合一定的原則進行設計阱穗,否則就不能稱為RESTful風格的API饭冬。因為許多人并沒有對REST進行充分的了解就宣稱自己的API是RESTful風格的API,以至于RESTful的提出者Fielding博士本人無法忍受揪阶,在2008年為此專門寫了一篇博客『REST APIs must be hypertext-driven』昌抠,hypertext-driven與HATEOAS是同一個概念的不同表述,在下文會進行闡述鲁僚。

2.1 RESTful

REST不是一種協(xié)議炊苫,也不是一種文件格式,更不是一種開發(fā)框架冰沙。它是一系列的設計約束的集合:無狀態(tài)性侨艾、將超媒體作為應用狀態(tài)的引擎等。REST是Representation State Transfer的縮寫倦淀,中文是『表述性狀態(tài)轉移』蒋畜,這里就涉及到資源的表述與狀態(tài)兩個概念。

簡單地說撞叽,資源可以看作是服務器上存儲的所有數(shù)據(jù)姻成,資源的表述則是服務器對外提供的指向這些資源的方式,使用JSON愿棋、XML等均可科展,一個資源可以有多種表述;資源的狀態(tài)則是服務器的數(shù)據(jù)存儲狀態(tài)糠雨,例如在t時刻才睹,服務器中存儲了m條數(shù)據(jù),這時候客戶端向服務端提交了一個創(chuàng)建數(shù)據(jù)的請求,服務器處理了此請求并創(chuàng)建了一條數(shù)據(jù)琅攘,那么在t+1時刻垮庐,服務器中就存儲了m+1條數(shù)據(jù),這兩個時刻的資源狀態(tài)就是不一樣的坞琴,t時刻發(fā)生的請求導致了資源狀態(tài)的改變哨查。

2.2 HATEOAS

Hypermedia As The Engine Of Application State,超媒體作為應用程序狀態(tài)的引擎剧辐。這是REST區(qū)別于其他SOA風格的主要特點寒亥。客戶端與服務端進行互動的時候荧关,完全是通過服務端動態(tài)提供的超媒體進行的溉奕。除了對超媒體的一般理解,客戶端不需要知道其他額外的知識忍啤。相反加勤,在一些SOA接口的設計中,客戶端與服務端的通信是要事先進行約定的同波,例如通過文檔或者接口描述語言(Interface Description Language, IDL)胸竞。而基于HTTP協(xié)議的REST設計里,一般采用的就是請求與響應的Header來體現(xiàn)HATEOAS原則(具體請參考:https://en.wikipedia.org/wiki/HATEOAS)参萄。這里也隱含這樣一層含義:REST應盡可能地利用HTTP標準中現(xiàn)有的東西,例如Header煎饼、標準方法與狀態(tài)碼讹挎。

從標準的角度看,HTTP標準是一項RFC標準吆玖,世界認可筒溃;而其他自定義的SOA標準則可能是一項個人標準或者公司標準,最多是一項互聯(lián)網草案(這對大部分公司來說都不可能)沾乘,而一項標準越是被廣為認可接受怜奖,其實現(xiàn)的通用性就越強。個人標準和公司標準都五花八門翅阵,這樣對每一個標準都要參照其相關文檔實現(xiàn)相應的行為邏輯是很麻煩的歪玲。

2.3 安全性

一個方法被調用1次與被調用0次是一樣的,此方法就是安全的掷匠,否則就是不安全的滥崩。例如,一個方法A僅僅是讀取數(shù)據(jù)讹语,并不創(chuàng)建或者修改數(shù)據(jù)钙皮,不論A方法被調用多少次,都不對數(shù)據(jù)記錄產生任何影響,A方法是安全的短条。而假如有另一個方法B對數(shù)據(jù)進行刪除导匣,B方法被調用1次后,數(shù)據(jù)會被刪除(或者標識位被修改)茸时,系統(tǒng)里的數(shù)據(jù)發(fā)生了變化贡定,那么B方法是不安全的。

2.4 冪等性

一個方法被同樣地調用1次與被調用多次是一樣的屹蚊,即同樣的輸入會得到同樣的輸出厕氨,此方法就是冪等的,否則就不是冪等的汹粤。

2.3節(jié)中A方法與B方法都是冪等的命斧,一個安全的方法一定是冪等的,一個冪等的方法不一定是安全的嘱兼。

假設一個方法C對某個全局計數(shù)器執(zhí)行自增操作并寫入數(shù)據(jù)庫国葬,每次調用C方法都會對系統(tǒng)數(shù)據(jù)產生影響,那么C方法就不是冪等的芹壕。

3 URL命名

URL用于標識資源汇四,因此URL應該以名詞進行命名,例如/users, /users/children等踢涌。

一般URL會內嵌參數(shù)通孽,例如要獲取id為313的user的信息,那么URL應該為/users/313睁壁,前面的user采用復數(shù)背苦,如果要列出其所有后代,則URL應為/users/313/children潘明,children為復數(shù)形式行剂,如果要獲取其id為499的后代,則URL應為/users/313/children/499

4 消息實體

消息實體钳降,就是請求和響應消息中的entity-body(也稱為body)厚宰,消息實體采用JSON字符串格式。

5 請求

5.1 方法

使用HTTP標準定義的請求方法遂填。

5.1.1 get

獲取資源铲觉,單個參數(shù)一般寫在URL上,多個參數(shù)則作為query parameter附在URL后面吓坚,例如:

  • 單個參數(shù):/user/123, 表示id為123的user

  • 多個參數(shù):/user?name=tom&phone=13787890987&gender=male

get方法應為冪等的备燃,并且不對數(shù)據(jù)記錄產生影響。對于漢字與特殊字符凌唬,應該進行urlencode并齐。

5.1.2 post

創(chuàng)建資源漏麦,請求的headers里設置Content-typeapplication/json,參數(shù)為json類型况褪。

根據(jù)約定,在創(chuàng)建成功之后测垛,返回的狀態(tài)碼應該是201(Created)捏膨,并且在response的Header里設置Location為新創(chuàng)建的資源的URL,例如食侮,創(chuàng)建了一個新的user号涯,該user創(chuàng)建后id為888,那么Header里應該設置Location/users/888锯七,當然链快,這應該是一個完整的URL,這里只是給出了一個相對路徑的URI以作為說明眉尸。返回了這些數(shù)據(jù)后域蜗,客戶端可以自定義后續(xù)行為,或者查看創(chuàng)建后的user噪猾,或者刷新當前的user列表霉祸,這些行為服務端并不關心。

如果重復提交了相同的數(shù)據(jù)袱蜡,第一次應該返回201丝蹭,以后則應返回409(Conflict),并且在response的Header里設置Location指向已經存在的資源坪蚁,說明沖突的來源半夷。

5.1.3 put

更新資源,對現(xiàn)有資源進行修改迅细,請求的headers與post一樣,參數(shù)也是淘邻。此方法應該是冪等的茵典。

5.1.4 delete

刪除資源。此方法應是冪等的宾舅。

5.2 Header

Content-type應設為application/json统阿。

另外應設置一個version,指明所使用的接口版本筹我。這不屬于HTTP協(xié)議中的一部分扶平,是自定義的,出于版本控制的考量蔬蕊,具體見第七章结澄。

5.3 body

采用JSON字符串,具體的結構有待商定,這不屬于HTTP協(xié)議的一部分麻献,是自定義的们妥。

這里主要放置業(yè)務相關的數(shù)據(jù)。

6 響應

6.1 Header

根據(jù)響應的狀態(tài)碼不同勉吻,相應地設置頭部监婶,具體見下一節(jié)。

但是在我所了解的公司里齿桃,做法都是統(tǒng)一返回200惑惶,然后在返回的JSON字符串里設置消息碼。我是不能理解的短纵。據(jù)一位前端同學說带污,前端代碼接收到了請求以后,不方便獲取Http狀態(tài)碼踩娘。其實我也寫過前端刮刑,不深入,但是一些基本的知識還是有的养渴,我覺得這并不難做到雷绢,估計是他的代碼封裝的時候沒有考慮到這一點,現(xiàn)在要改比較麻煩理卑,所以不想大動干戈翘紊、傷筋動骨。

6.2 狀態(tài)碼

狀態(tài)碼 語義 使用場景
200 OK 正常返回消息藐唠,什么問題也沒有
201 Created 創(chuàng)建資源成功帆疟,Header里應設置Location指向新創(chuàng)建的資源
202 Accepted 請求已被接收,但是處理過程較長宇立,不能馬上返回結果
304 Not Modified 沒有任何修改發(fā)生
401 Unauthorized 缺乏權限踪宠,指已經登錄但是缺乏請求這個資源的權限
403 Forbidden 拒絕訪問,可用于未登錄時攔截返回的狀態(tài)碼妈嘹,此時Header里應設置Location為登錄頁面的URL
404 Not Found 不存在所請求的資源
406 Not Acceptable 請求沒有被接收柳琢,參數(shù)約束校驗不通過,或者其他業(yè)務類型的錯誤都可以返回這個狀態(tài)碼润脸,response的body里應有表示錯誤信息的JSON實體柬脸。
409 Conflict 請求的資源有沖突,例如多次提交一樣的創(chuàng)建請求毙驯,response的Header里應設置Location為產生沖突的資源的URL
500 Internal Server Error 服務器的非業(yè)務類錯誤倒堕,response的body里應有表示錯誤信息的JSON實體

6.3 body采用JSON字符串。

JSON的結構分為兩種:成功爆价、失敗垦巴。

一般而言媳搪,只有返回200的時候才需要讀取成功的JSON,只有返回406和500的時候才需要讀取失敗的JSON魂那,對于其他的狀態(tài)碼蛾号,客戶端不需要服務器提供額外的消息。

對于成功的JSON涯雅,里面應該只包含一個result對象鲜结,而失敗的JSON應該使用這樣的結構:


{

error: {

code: xxx,

message: "xxx",

data: {...}

}

}

失敗的JSON只有一個error對象,包含錯誤碼活逆、消息及相關數(shù)據(jù)精刷,message應該是直接可讀的消息,客戶端毋需理解發(fā)生了什么錯誤蔗候,客戶端只需將消息展示出來即可怒允。在收到406的時候,客戶端只需知道發(fā)生的錯誤是由客戶端造成的即可锈遥,具體是什么類型并不需要知道纫事,將消息直接展示出來,讓使用的人知道是什么即可所灸,所以message應該是人類可以理解的文本丽惶。同理,收到500的時候爬立,只需知道這個錯誤是服務端的問題即可钾唬,客戶端也毋需知道具體的錯誤類型,最多就將錯誤碼和消息展示出來侠驯,讓使用者有反饋的依據(jù)即可抡秆。

7 版本控制

考慮到接口有可能升級,升級的類型有幾種:

  1. 新增功能接口

  2. 原有接口返回數(shù)據(jù)增加字段

  3. 現(xiàn)有接口返回數(shù)據(jù)變更現(xiàn)有字段格式或刪除現(xiàn)有字段

  4. 現(xiàn)有接口變更業(yè)務邏輯

  5. 刪除接口

其中吟策,前兩種升級并不會影響客戶端儒士,因此毋需處理。而后面三種會導致使用舊接口的客戶端不能正常工作檩坚。

一般服務端升級與客戶端升級都不是同步的着撩,客戶端升級往往會滯后,因此在服務端升級后應該保留舊版本的接口繼續(xù)運行一段時間效床,讓未升級的客戶端可以繼續(xù)工作一段時間,同時可以上線新版本的客戶端权谁。過一段時間后再將舊版本的接口下線剩檀。

而版本控制應該是向下兼容的,即假設當前版本是1.2旺芽,如果客戶端請求1.3版本的服務沪猴,應當用當前版本提供服務辐啄。如果沒有注明請求的版本號,應當提供當前版本的服務运嗜。

一般情況下壶辜,客戶端請求需要帶版本號,但是服務端并不需要對此進行處理担租,除非是同時運行新舊版本的同一個接口砸民,才需要做差異處理。

8 實現(xiàn)

8.1 Spring HATEOAS

Spring HATEOAS可以很方便地與Spring MVC結合來開發(fā)RESTful接口奋救。具體參照其文檔:

http://docs.spring.io/spring-hateoas/docs/0.20.0.RELEASE/reference/html/#fundamentals.jaxb-json

原文鏈接:
https://bungder.github.io/2017/07/24/REST/

我的技術博客:
https://bungder.github.io


為什么簡書的MarkDown不支持表格語法......

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末岭参,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子尝艘,更是在濱河造成了極大的恐慌演侯,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件背亥,死亡現(xiàn)場離奇詭異秒际,居然都是意外死亡,警方通過查閱死者的電腦和手機狡汉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門娄徊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人轴猎,你說我怎么就攤上這事嵌莉。” “怎么了捻脖?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵锐峭,是天一觀的道長。 經常有香客問我可婶,道長沿癞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任矛渴,我火速辦了婚禮椎扬,結果婚禮上,老公的妹妹穿的比我還像新娘具温。我一直安慰自己蚕涤,他們只是感情好,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布铣猩。 她就那樣靜靜地躺著揖铜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪达皿。 梳的紋絲不亂的頭發(fā)上天吓,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天贿肩,我揣著相機與錄音,去河邊找鬼龄寞。 笑死汰规,一個胖子當著我的面吹牛,可吹牛的內容都是我干的物邑。 我是一名探鬼主播溜哮,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拂封!你這毒婦竟也來了茬射?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤冒签,失蹤者是張志新(化名)和其女友劉穎在抛,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體萧恕,經...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡刚梭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了票唆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朴读。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖走趋,靈堂內的尸體忽然破棺而出衅金,到底是詐尸還是另有隱情,我是刑警寧澤簿煌,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布氮唯,位于F島的核電站,受9級特大地震影響姨伟,放射性物質發(fā)生泄漏惩琉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一夺荒、第九天 我趴在偏房一處隱蔽的房頂上張望瞒渠。 院中可真熱鬧,春花似錦技扼、人聲如沸伍玖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窍箍。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間仔燕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工魔招, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留晰搀,地道東北人。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓办斑,卻偏偏與公主長得像外恕,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子乡翅,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理鳞疲,服務發(fā)現(xiàn),斷路器蠕蚜,智...
    卡卡羅2017閱讀 134,633評論 18 139
  • 一說到REST尚洽,我想大家的第一反應就是“啊,就是那種前后臺通信方式靶累∠俸粒”但是在要求詳細講述它所提出的各個約束,以及如...
    時待吾閱讀 3,415評論 0 19
  • 原文鏈接:https://https://howardwchen.com/2017/09/18/talk-abou...
    守望者Howard閱讀 6,339評論 1 6
  • 01 “先生挣柬!我的手指甲斷了潮酒!”七七將手指往閔先生的眼下伸去。 閔先生拉起那雙小手邪蛔,粉嫩粉嫩的指甲此時滲著一滴滴地...
    薩摩七七閱讀 434評論 12 0
  • 小豬微信淘客官網:http://www.pigtk.com 為新手淘客提供完整的微信淘寶客賺錢的整套解決方案急黎。 阿...
    朝陽奔跑的塔塔閱讀 967評論 0 1