淺談RESTful接口設(shè)計(jì)和開發(fā)

一、由來(lái)和簡(jiǎn)介

現(xiàn)在我們團(tuán)隊(duì)內(nèi)說(shuō)開發(fā)服務(wù)端接口赚哗,一般不做特別強(qiáng)調(diào)浇雹,都指的是 RESTful API沉御。那RESTful API是什么,怎么設(shè)計(jì)和開發(fā)呢昭灵?雖然網(wǎng)絡(luò)上已經(jīng)有大量相關(guān)的資料吠裆,但我們?cè)谧稣衅该嬖嚂r(shí),發(fā)現(xiàn)有不少同學(xué)不是很理解烂完,或是在使用時(shí)不會(huì)變通试疙、受其束縛,所以這個(gè)話題需要繼續(xù)聊聊抠蚣,介紹基本概念的同時(shí)分享一下實(shí)踐經(jīng)驗(yàn)祝旷。

軟件開發(fā)從單機(jī)軟件到互聯(lián)網(wǎng)化,從互聯(lián)網(wǎng)單體應(yīng)用到系統(tǒng)拆分嘶窄、前后端分離怀跛、服務(wù)化,接著到后來(lái)移動(dòng)互聯(lián)網(wǎng)興起柄冲,一度有“mobile first”的策略吻谋,再到現(xiàn)在的微服務(wù)∠趾幔可以說(shuō)軟件架構(gòu)越來(lái)越龐大復(fù)雜漓拾,相關(guān)聯(lián)的各部分需要?jiǎng)澐值酶?xì)、職責(zé)更專一长赞,這樣各部分之間的交互就很頻繁晦攒。RESTful作為一種架構(gòu)風(fēng)格闽撤、一種接口設(shè)計(jì)規(guī)范得哆,簡(jiǎn)單、標(biāo)準(zhǔn)哟旗、易擴(kuò)展讓其得到了越來(lái)越多的應(yīng)用贩据。

REST 全稱 “Representational State Transfer”,乍看很晦澀的名字闸餐,翻譯為“表現(xiàn)層狀態(tài)轉(zhuǎn)化”饱亮,全稱應(yīng)叫“資源表現(xiàn)層狀態(tài)轉(zhuǎn)化””∩嵘常“資源”指服務(wù)中的實(shí)體或信息近上,“表現(xiàn)層”指信息的展現(xiàn)形式,如json拂铡、xml壹无、html等葱绒,“狀態(tài)轉(zhuǎn)化”指接口訪問(wèn)過(guò)程中數(shù)據(jù)和狀態(tài)的變化,作為http無(wú)狀態(tài)服務(wù)斗锭,業(yè)務(wù)的實(shí)現(xiàn)必然要涉及到數(shù)據(jù)和狀態(tài)的變化地淀。

二、資源

在RESTful API中岖是,“資源” 使用名詞形式的URL表示帮毁,且一般為復(fù)數(shù),如“/users”豺撑,使用具有“ID”同等作用的數(shù)據(jù)限定單體烈疚,如 “/users/666”或”/users/zhangsan”,使用URL連接表示所屬或關(guān)聯(lián)關(guān)系 如 ”/users/666/projects”表示ID為666用戶的所有項(xiàng)目前硫。

接口的版本號(hào)胞得,一般也放在URL中,簡(jiǎn)單方便屹电,易于監(jiān)控阶剑, 如”/v1/users”。 也有將版本號(hào)放在http header中危号,或者有的將主版本號(hào)放URL中牧愁,小版本好放httpheader中。

三外莲、表現(xiàn)層

“表現(xiàn)層”一般采用JSON字符串猪半,作為接口數(shù)據(jù)傳輸,相對(duì)于XML偷线,JSON雖然在表現(xiàn)力上若一些磨确,但可讀性、數(shù)據(jù)簡(jiǎn)潔性上都好很多声邦,且和前端Javascript天生就是一對(duì)乏奥。接口返回最好采用統(tǒng)一的格式(如:{code: 200, msg:”O(jiān)K”,data:{}}),方便使用者亥曹。其中“code”可以表示特殊業(yè)務(wù)相關(guān)的狀態(tài)邓了,如定義code為600時(shí)表示添加的用戶名稱重復(fù),在無(wú)特殊含義時(shí)建議和http status保持一致以減少認(rèn)知上的負(fù)擔(dān)媳瞪;”msg”給出必要的簡(jiǎn)短描述骗炉,在某些場(chǎng)景考慮到安全性,可以簡(jiǎn)化甚至將“msg”留空蛇受,如一個(gè)未授權(quán)的用戶訪問(wèn)資源句葵,接口返回http status為401, “msg”不要再做出過(guò)于詳細(xì)的原因說(shuō)明;“data”可以是對(duì)象或列表,若是分頁(yè)的列表乍丈,采用統(tǒng)一的分頁(yè)數(shù)據(jù)格式說(shuō)明分頁(yè)信息——如當(dāng)前頁(yè)碼熊响、總頁(yè)數(shù)、總條數(shù)诗赌。

四汗茄、狀態(tài)轉(zhuǎn)移

“狀態(tài)轉(zhuǎn)移”可以使用基本的httpmethod和上面的URL形成一個(gè)動(dòng)賓結(jié)構(gòu),如“POST /v1/users”表示創(chuàng)建一個(gè)用戶铭若,在http body中提交用戶詳細(xì)信息洪碳,成功后接口返回 {code:200, msg:”O(jiān)K”,data:{id:666, name:”zhangsan”}} 。 一般常用的有GET叼屠、POST瞳腌、PUTDELETE*镜雨,分別表示獲取嫂侍、創(chuàng)建、更新荚坞、刪除挑宠,而HEAD表示獲取接口元數(shù)據(jù)。對(duì)于GET颓影、PUT各淀、DELETE、HEAD要實(shí)現(xiàn)接口的 冪等性诡挂,而POST請(qǐng)求根據(jù)業(yè)務(wù)需要也可以實(shí)現(xiàn)其冪等性碎浇,根據(jù)系統(tǒng)實(shí)現(xiàn)復(fù)雜度,POST請(qǐng)求實(shí)現(xiàn)冪等性難度會(huì)更大些璃俗,特別是在微服務(wù)場(chǎng)景下奴璃。

五、RESTful規(guī)范帶來(lái)的一些 “困境”

采用一套規(guī)范的同時(shí)往往也意味著有限制城豁,會(huì)對(duì)接口設(shè)計(jì)造成一些困境苟穆。

http method有限的幾個(gè)動(dòng)詞,有時(shí)不足以表達(dá)接口含義钮蛛,如“訂閱一個(gè)項(xiàng)目”(在項(xiàng)目更新時(shí)給訂閱者發(fā)送通知)鞭缭,對(duì)資源”/v1/users/666/projects/1”加任何一個(gè)httpmethod都不足以表達(dá)剖膳。此時(shí)可以采用變通的方法:一般可以在資源路徑末尾加動(dòng)詞或?qū)⒉僮鲃?dòng)作抽象為一種實(shí)體對(duì)象魏颓,抽象出來(lái)的實(shí)體對(duì)象盡量采用常規(guī)的名稱,要易于理解,避免異想天開造輪子吱晒。如POST /v1/users/666/projects/1/subscribe 表示訂閱用戶666的ID為1的項(xiàng)目甸饱,POST /v1/users/666/projects/1/star 則將“收藏項(xiàng)目”這個(gè)動(dòng)作抽象為“給項(xiàng)目加一顆星”。

再比如對(duì)“/projects”有復(fù)雜的查詢需求,URL用任何一個(gè)“資源”路徑都不好表達(dá)叹话,且查詢參數(shù)還比較多——此時(shí)就不要再固守“查詢只能用GET”偷遗,可以設(shè)計(jì)為POST /v1/projects/search,查詢參數(shù)太多不適合當(dāng)作queryparameter 放在URL后面則可以放到http body中驼壶。

考慮到很多監(jiān)控系統(tǒng)氏豌,不會(huì)去讀取httpPOST請(qǐng)求的body內(nèi)容,在很需要做監(jiān)控的接口上热凹,甚至可以考慮將POST請(qǐng)求的body部分改為在URL的query參數(shù)泵喘,如 POST /tasks?name=xxx&template=yyyy 。對(duì)于固守規(guī)范的同學(xué)會(huì)很詫異既然用了POST 怎么還將參數(shù)放到URL后面——在實(shí)際需要很大時(shí)般妙,是可以犧牲一些設(shè)計(jì)上的“完美”的纪铺。

一般來(lái)說(shuō),每個(gè)接口職責(zé)要清晰明確碟渺,業(yè)務(wù)方根據(jù)需要去組合編排這些接口來(lái)實(shí)現(xiàn)業(yè)務(wù)鲜锚。在某些必要的場(chǎng)景也可以將多個(gè)簡(jiǎn)單接口的功能組合到一個(gè)大的接口中,以減少接口訪問(wèn)在時(shí)間和性能上的損耗苫拍。但這樣的做法不能太多芜繁,否則接口容易交叉、混亂绒极、職責(zé)不清浆洗。

總之,不要死抱著規(guī)范不放集峦,在必要的地方要做變通伏社,多看看一些好的示例學(xué)習(xí),如github的接口塔淤。

六摘昌、領(lǐng)域模型分析和RESTful API設(shè)計(jì)的關(guān)系

在得到一個(gè)業(yè)務(wù)系統(tǒng)需求后,在開發(fā)服務(wù)接口之前高蜂,我們會(huì)有一步是對(duì)其做領(lǐng)域模型分析聪黎,提煉出該業(yè)務(wù)系統(tǒng)涉及的各種實(shí)的或需的領(lǐng)域?qū)ο螅⒆R(shí)別它們之間的關(guān)系备恤。對(duì)某些業(yè)務(wù)系統(tǒng)來(lái)說(shuō)稿饰,完成這些領(lǐng)域?qū)ο蟮腃RUD操作及它們之間關(guān)系的組合變化,就能支撐大部分的業(yè)務(wù)需求露泊。那這和RESTful API 有什么關(guān)系呢喉镰?相信到這里你不難發(fā)現(xiàn),RESTful 規(guī)范中URL表示的“資源”惭笑、“資源”間的關(guān)聯(lián)關(guān)系侣姆,以及“狀態(tài)轉(zhuǎn)移”部分說(shuō)到的http method + URL的動(dòng)賓組合生真,都能比較好的繼承我們領(lǐng)域模型分析的結(jié)果,很好的過(guò)度到RESTful API的設(shè)計(jì)捺宗。當(dāng)然柱蟀,不表示領(lǐng)域模型里的所有“實(shí)體”都會(huì)對(duì)應(yīng)到RESTful API中的URL,在接口設(shè)計(jì)中結(jié)合實(shí)際情況肯定會(huì)有相應(yīng)的取舍和變化蚜厉。比如領(lǐng)域模型中分析出來(lái)的某個(gè)實(shí)體可能只是體現(xiàn)在數(shù)據(jù)存儲(chǔ)層长已,沒到接口層,或是這個(gè)所謂“實(shí)體”其實(shí)是多個(gè)概念的綜合表現(xiàn)昼牛,體現(xiàn)為多個(gè)小的實(shí)體及其關(guān)系痰哨。

通過(guò)領(lǐng)域模型分析,結(jié)合DB數(shù)據(jù)層技術(shù)上的考量匾嘱,可以將需要落庫(kù)的對(duì)象進(jìn)一步分析得到DB的ER圖設(shè)計(jì)斤斧。至此,至下往上從數(shù)據(jù)層霎烙、之上往下從接口層都清晰了后撬讽,中間層的代碼就容易組織得清晰,而一個(gè)新來(lái)的同學(xué)也比較容易在這個(gè)框架內(nèi)去理解和擴(kuò)展悬垃。

七游昼、RESTful API設(shè)計(jì)對(duì)開發(fā)的影響

在一個(gè)團(tuán)隊(duì)中,如果沒有清晰的系統(tǒng)設(shè)計(jì)尝蠕、接口設(shè)計(jì)烘豌,特別是新人,在拿到一個(gè)業(yè)務(wù)需求時(shí)看彼,容易僅僅面向當(dāng)前需求來(lái)編寫代碼廊佩,缺少一個(gè)通用的、長(zhǎng)遠(yuǎn)的考慮靖榕。如要提供一個(gè)接口給xxx系統(tǒng)“創(chuàng)建任務(wù)”标锄,接口名甚至可能被定義為/createXxxTask,接口內(nèi)部邏輯實(shí)現(xiàn)上自然也會(huì)加上針對(duì)這個(gè)業(yè)務(wù)的一堆if else茁计×匣剩可以想象在接入的業(yè)務(wù)需求多了后,會(huì)有很多同質(zhì)化的接口星压,也會(huì)存在不少大同小異的代碼重復(fù)践剂。

轉(zhuǎn)換一個(gè)角度,理解要實(shí)現(xiàn)的業(yè)務(wù)需求后娜膘,將目光由外轉(zhuǎn)到系統(tǒng)內(nèi)逊脯。根據(jù)以上提到的領(lǐng)域模型分析、RESTful API設(shè)計(jì)劲绪,我們對(duì)當(dāng)前系統(tǒng)有哪些“資源”男窟,他們組合關(guān)系是怎樣的,進(jìn)而得出系統(tǒng)目前能提供哪些能力贾富,還欠缺什么歉眷,然后再在這上面去擴(kuò)展。盡量將這個(gè)需求本質(zhì)上要操作哪些資源和關(guān)系提煉出來(lái)颤枪,設(shè)置更加通用和可擴(kuò)展的接口汗捡。

相對(duì)于天馬行空無(wú)章法地接口設(shè)計(jì),RESTful接口設(shè)計(jì)規(guī)范在給設(shè)計(jì)著開發(fā)者戴上一副腳鐐的同時(shí)也讓其舞得更美畏纲。

感興趣的可以進(jìn)一步了解 接口身份認(rèn)證扇住、HATEOAS**、各種對(duì)數(shù)據(jù)的序列化方式以及RPC 方案和RESTfulAPI 方案在技術(shù)選型上的取舍盗胀。

接口設(shè)計(jì)過(guò)程中可以使用一些工具艘蹋、文檔來(lái)沉淀設(shè)計(jì)成果,同時(shí)用于加強(qiáng)在設(shè)計(jì)票灰、開發(fā)過(guò)程中團(tuán)隊(duì)之間的交流協(xié)作 —— 這部分內(nèi)容留待下次再聊女阀。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市屑迂,隨后出現(xiàn)的幾起案子浸策,更是在濱河造成了極大的恐慌,老刑警劉巖惹盼,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件庸汗,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡手报,警方通過(guò)查閱死者的電腦和手機(jī)蚯舱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)掩蛤,“玉大人晓淀,你說(shuō)我怎么就攤上這事≌档担” “怎么了凶掰?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蜈亩。 經(jīng)常有香客問(wèn)我懦窘,道長(zhǎng),這世上最難降的妖魔是什么稚配? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任畅涂,我火速辦了婚禮,結(jié)果婚禮上道川,老公的妹妹穿的比我還像新娘午衰。我一直安慰自己立宜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布臊岸。 她就那樣靜靜地躺著橙数,像睡著了一般。 火紅的嫁衣襯著肌膚如雪帅戒。 梳的紋絲不亂的頭發(fā)上灯帮,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音逻住,去河邊找鬼钟哥。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瞎访,可吹牛的內(nèi)容都是我干的腻贰。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼扒秸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼银受!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起鸦采,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤宾巍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后渔伯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體顶霞,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年锣吼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了选浑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡玄叠,死狀恐怖古徒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情读恃,我是刑警寧澤隧膘,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站寺惫,受9級(jí)特大地震影響疹吃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜西雀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一萨驶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧艇肴,春花似錦腔呜、人聲如沸叁温。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)膝但。三九已至,卻和暖如春膛檀,著一層夾襖步出監(jiān)牢的瞬間锰镀,已是汗流浹背娘侍。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工咖刃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人憾筏。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓嚎杨,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親氧腰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子枫浙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

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