這回來(lái)講講后臺(tái)接口的設(shè)計(jì)阁簸。
可能有同學(xué)會(huì)覺得后臺(tái)的接口和我們大前端開發(fā)有什么關(guān)系爬早?試想一下哼丈,在碰到一些不合理的接口設(shè)計(jì)的時(shí)候启妹,你們開發(fā)是否覺得很別扭——需要為了坑爹的接口寫很多臟代碼引坑?甚至醉旦,這么開發(fā)出來(lái)的頁(yè)面饶米,體驗(yàn)也會(huì)很差?我們不是說(shuō)硬無(wú)理要求后端接口按照前端業(yè)務(wù)去封裝车胡,而是說(shuō)為了項(xiàng)目更好地發(fā)展檬输,為了用戶能有更棒的體驗(yàn),應(yīng)該有討論商量的空間匈棘。一些差勁的設(shè)計(jì)丧慈,應(yīng)該被拒絕。
本文使用前端來(lái)指代 Android主卫、iOS 以及 Web逃默。
本文不是教大家撕逼的(趕緊撇清關(guān)系)。
全局
全局指所有接口統(tǒng)一的規(guī)范簇搅。
請(qǐng)求頭
應(yīng)該使用http header來(lái)放置通用性的參數(shù)完域,比如:
- APPID(Android/iOS/H5)
- APPVER(版本號(hào))
- APP-BUILD-NUM(內(nèi)部小版本號(hào))
- TOKEN
- NETWORK(網(wǎng)絡(luò)環(huán)境)
- LANGUAGE(語(yǔ)言)
- 等等
前端使用 POST
鍵值對(duì)方式提交給后端,可以使用 RawJSON
格式瘩将。
Content-Type
設(shè)為 application/x-www-form-urlencoded
或者 application/json
吟税。
全局響應(yīng)格式
響應(yīng)格式應(yīng)該統(tǒng)一凹耙,方便前端做統(tǒng)一的處理,尤其是數(shù)據(jù)字段肠仪,應(yīng)該統(tǒng)一放在一個(gè)map里面肖抱。
名字 | 類型 | 詳細(xì)描述 |
---|---|---|
status_no | INT | 狀態(tài)碼 |
status_msg | STRING | 狀態(tài)信息 |
data | MAP | 響應(yīng)內(nèi)容 |
time | INT | 響應(yīng)時(shí)間戳 |
狀態(tài)碼
全局應(yīng)該定義統(tǒng)一的狀態(tài)碼(status_code),而不應(yīng)該每個(gè)接口單獨(dú)去定義藤韵。
具體規(guī)則可以自行定義虐沥,比如0為正確,負(fù)數(shù)為錯(cuò)誤泽艘。
常見的錯(cuò)誤狀態(tài)碼有
- 普通異常
- token不合法欲险,需要重新登錄
- 重復(fù)登錄
- 需要完善個(gè)人信息
- 第三方賬號(hào)登陸,需要綁定官方賬號(hào)
- 請(qǐng)求頭不合法(版本號(hào)匹涮,APPID等)
- 數(shù)據(jù)解密錯(cuò)誤
可以根據(jù)錯(cuò)誤類型劃分使用的區(qū)域段天试,如登陸系列使用 -1000 到 -1999 區(qū)域。
如此定義后然低,前端可以進(jìn)行全局的統(tǒng)一處理喜每,如重復(fù)登陸則踢出用戶。
錯(cuò)誤信息
除了特殊的錯(cuò)誤信息——如重復(fù)登錄雳攘、token不合法這些狀態(tài)碼對(duì)應(yīng)的带兜,以及無(wú)網(wǎng)、沒(méi)數(shù)據(jù)這些吨灭,對(duì)于通用的異常刚照,應(yīng)該由后臺(tái)返回錯(cuò)誤信息。
統(tǒng)一data字段
data 字段應(yīng)該統(tǒng)一放在一個(gè) map 內(nèi)喧兄,里面存放具體的響應(yīng)信息无畔。
Scheme
全局定義統(tǒng)一的 scheme(Deeplink),方便前端進(jìn)行跳轉(zhuǎn)吠冤。
前端只需要定義自己唯一的 Deeplink 并進(jìn)行注冊(cè)即可(scheme 和 host)浑彰。
具體使用 REST 風(fēng)格(如 markzhai://article/XXX),還是普通的 urlencode (如 markzhai://article/?id=XXX&redirect_url=XXX)可以根據(jù)自身需求定義拯辙。
使用 REST 風(fēng)格的一個(gè)顧慮是可能 scheme 本身并不是基于資源的郭变,而是基于類型、行為等涯保,所以 urlencode 可能更通用诉濒,但相應(yīng)地基于 Deeplink 的資源索引會(huì)希望你是無(wú)狀態(tài)的 REST 風(fēng)格。
回傳 or 狀態(tài)碼
應(yīng)該使用回傳還是狀態(tài)碼呢遭赂?比如點(diǎn)贊消息循诉,是應(yīng)該回傳一個(gè) status_code,0則表示點(diǎn)贊成功撇他,還是應(yīng)該回傳現(xiàn)在的贊狀態(tài)呢茄猫?
其實(shí)這兩者對(duì)于后臺(tái)的性能來(lái)說(shuō)狈蚤,是幾乎沒(méi)有影響的,因?yàn)槿〉玫闹皇切薷牡淖侄蔚淖詈蠼Y(jié)果划纽。但是對(duì)前端來(lái)說(shuō)脆侮,差別就有了——需要維護(hù)狀態(tài)。
舉一個(gè)例子:
A 和 B 是兩個(gè)用戶勇劣,B 關(guān)注了 A靖避,A 沒(méi)有關(guān)注 B。
A 看 B 的主頁(yè)的時(shí)候比默,顯示關(guān)系是 未關(guān)注幻捏,此時(shí) A 點(diǎn)擊了關(guān)注,如果沒(méi)有回傳信息命咐,那么我們只能把關(guān)系刷新為 已關(guān)注篡九,而沒(méi)有足夠的信息去刷新為 互相關(guān)注。否則就需要前端去做惡心的邏輯(后端一開始用戶關(guān)系就需要傳 B 關(guān)注了 A)醋奠,根據(jù)原來(lái)的關(guān)系去做切換榛臼,還要在失敗的時(shí)候刷回原來(lái)的狀態(tài)。
一些有豐富經(jīng)驗(yàn)的后端會(huì)在這種接口使用回傳窜司,因?yàn)樗麄冎绤^(qū)別沛善。
模塊vs頁(yè)面
在后臺(tái)的接口設(shè)計(jì)上,又分為了按頁(yè)面以及按模塊塞祈。
按頁(yè)面的接口盡可能讓前端一個(gè)頁(yè)面只請(qǐng)求一次金刁,一次返回所需要的全部信息;按模塊的接口在后端定義自己的業(yè)務(wù)模塊如用戶织咧、Feed胀葱、標(biāo)簽漠秋、搜索等笙蒙,并盡量避免模塊間的耦合。
從后端角度來(lái)說(shuō)庆锦,按模塊當(dāng)然是更好的(只需要?jiǎng)澐值貕蚣?xì)就好)捅位,到時(shí)候需求有什么變更,讓前端自己去改變接口的組合就好搂抒,自己高枕無(wú)憂艇搀。但從前端的角度來(lái)說(shuō),接口的組合涉及到異步之間的關(guān)系求晶,盡管RxJava這樣的響應(yīng)式編程框架讓異步簡(jiǎn)單了很多焰雕,但仍然希望可以避免,更嚴(yán)重的是芳杏,多次接口請(qǐng)求會(huì)讓前端的體驗(yàn)變差矩屁,并行接口的影響稍小辟宗,而一些有前置后置關(guān)系的接口則麻煩比較大,一個(gè)接著一個(gè)請(qǐng)求吝秕,會(huì)讓用戶等很久泊脐。即便是并行接口,有時(shí)候頁(yè)面的渲染仍然需要所有接口數(shù)據(jù)返回后才可以進(jìn)行烁峭。
但如果讓后端按照頁(yè)面去套容客,這樣在后端其實(shí)一樣有性能的損耗,需要一個(gè)頁(yè)面接口去單獨(dú)調(diào)用各個(gè)模塊的接口约郁,然后進(jìn)行組合缩挑。
究竟如何選擇呢?筆者認(rèn)為在服務(wù)器性能足夠的前提下鬓梅,后端應(yīng)該盡量減少頁(yè)面請(qǐng)求次數(shù)调煎,尤其是有依賴關(guān)系的串行請(qǐng)求。另一方面己肮,在一些影響不那么大的頁(yè)面士袄,則可以由前端自行進(jìn)行接口組合(比如上面是用戶主頁(yè)的用戶展示,下面是該用戶的 feed 列表)谎僻。
另外娄柳,如果你們有一個(gè)好的設(shè)計(jì)師,那么他應(yīng)該會(huì)貫徹一個(gè)地方只應(yīng)該以一樣?xùn)|西為主體艘绍,而不應(yīng)該去把亂七八糟的東西拼湊在一起赤拒。
分頁(yè)信息
現(xiàn)代的前端交互上,已經(jīng)很少會(huì)有頁(yè)碼顯示了诱鞠,所以很多后端的列表頁(yè)接口中挎挖,就沒(méi)有帶上了分頁(yè)的信息,而改讓客戶端去維護(hù)請(qǐng)求的頁(yè)碼航夺。
那么蕉朵,分頁(yè)信息在接口中,真的就沒(méi)有存在的必要了嗎阳掐?其實(shí)未必始衅。
為什么需要分頁(yè)信息
頁(yè)面大小(pageSize)可能改變(無(wú)論是前端自己的配置亦或是后臺(tái)修改)缭保,如果僅由客戶端維護(hù)頁(yè)碼汛闸,那么下次請(qǐng)求下一頁(yè)就會(huì)出錯(cuò),除非客戶端帶上自己上次的頁(yè)面大小艺骂。
如果客戶端不知道當(dāng)前頁(yè)碼和總頁(yè)數(shù)诸老,就無(wú)法在請(qǐng)求完判斷底部應(yīng)該顯示上拉加載更多還是沒(méi)數(shù)據(jù)了,導(dǎo)致必須再請(qǐng)求一次钳恕,根據(jù)是否返回 list 以及數(shù)據(jù)是否為空去進(jìn)行判斷别伏。
另外吮廉,由后端返回頁(yè)碼也避免了客戶端修改頁(yè)碼出錯(cuò)的可能。
但對(duì)后端來(lái)說(shuō)畸肆,這些信息的獲取卻意味著更大的計(jì)算和I/O資源損耗宦芦。
折中辦法
折中地,可以讓后端返回一個(gè) has_more
字段轴脐,這樣可以避免最后一次不必要的請(qǐng)求(尤其是數(shù)據(jù)都不夠顯示滿一頁(yè)的情況下)调卑,體驗(yàn)會(huì)好很多。盡管這樣仍然無(wú)法避免頁(yè)面大小改變的問(wèn)題大咱。
配置
一些后臺(tái)喜歡讓讓前端寫限制邏輯恬涧,比如搜索的關(guān)鍵字限制,各種過(guò)濾邏輯碴巾。
咱們先不提讓前端寫死這些邏輯的靈活性問(wèn)題(客戶端和網(wǎng)頁(yè)不同溯捆,不能那么方便地發(fā)版本,即便是網(wǎng)頁(yè)厦瓢,改代碼發(fā)版本就不用測(cè)試了嗎提揍?出了問(wèn)題你背?)煮仇。前端的輸入真的可以信任嗎劳跃?且不談代碼可能寫的不夠嚴(yán)謹(jǐn)導(dǎo)致輸入跳過(guò)了檢查,用戶還能root浙垫、越獄刨仑,甚至可以反編譯客戶端或者直接模擬請(qǐng)求。
所以良好的配置檢查應(yīng)該有兩種
- 后端下發(fā)配置字段夹姥,前端根據(jù)字段去做對(duì)應(yīng)檢查杉武。好處是減少后臺(tái)壓力,壞處是無(wú)法保證安全性辙售。
- 后端收到請(qǐng)求自行檢查過(guò)濾轻抱,如果出錯(cuò)則返回錯(cuò)誤信息給前端顯示。
毋庸置疑圾亏,后者更好十拣。
另外封拧,再說(shuō)說(shuō)靈活性志鹃。今天可能限制3個(gè)字,明天產(chǎn)品需求可能就是4個(gè)字泽西,現(xiàn)在產(chǎn)品/運(yùn)營(yíng)說(shuō)不會(huì)改曹铃,到時(shí)候難道就真的一定不會(huì)改嗎?
空字段
一些空字段捧杉,如果沒(méi)有陕见,服務(wù)端應(yīng)該返回一個(gè)空的默認(rèn)字段 比如 String 用""秘血,int 用 0,Object 用 {}评甜,Array 用 []灰粮,這樣減小前端校驗(yàn)?zāi)承┬r?yàn)漏了出現(xiàn)錯(cuò)誤的情況。
---- 由三帥泥阿布補(bǔ)充
我個(gè)人認(rèn)為這樣本身對(duì)流量損耗不大忍坷,且確實(shí)避免了很多可能的異常粘舟,是個(gè)很好的意見。當(dāng)然了佩研,正如后端不應(yīng)該相信前端的輸入一樣柑肴,前端也不能相信后端數(shù)據(jù)的完備性,仍然還是需要悲劇地去校驗(yàn)旬薯。
教訓(xùn)
不要相信什么以后重構(gòu)晰骑,接口現(xiàn)在這么說(shuō),以后他會(huì)告訴你绊序,沒(méi)法兼容老版本所以只能這樣了(甚至搞出兩套規(guī)則讓你同時(shí)兼容)硕舆。
不是說(shuō)后端就是老大。大家的目標(biāo)都是為了項(xiàng)目能做好骤公,而現(xiàn)在通常前端的壓力比后端更大(前端寫得頭昏腦花岗宣,后端網(wǎng)上東逛西逛),所以在不會(huì)很大影響性能的前提下淋样,應(yīng)該滿足前端的合理需求耗式。體驗(yàn)為先。(硬氣一點(diǎn)趁猴,老大應(yīng)該挺你刊咳,甚至親自去撕逼,大不了找CTO)
接口的頻繁修改要向上反饋儡司,測(cè)試數(shù)據(jù)不滿足要求也要及時(shí)提出娱挨。咱們不做背鍋俠。
靈活捕犬,靈活跷坝。做各種需求的時(shí)候,想一想碉碉,這兒會(huì)不會(huì)改變柴钻?就算現(xiàn)在不會(huì)變,以后就不會(huì)變嗎垢粮?比如抽屜里的入口贴届,是不是要做成可配置的?多問(wèn)問(wèn),實(shí)現(xiàn)上盡量靈活毫蚓。
總結(jié)
本篇講了很多通用的后端接口設(shè)計(jì)問(wèn)題占键。幫助大家在面對(duì)一些不合理的接口設(shè)計(jì)時(shí),能進(jìn)行友善的討論(撕逼)元潘,讓項(xiàng)目能做得更好畔乙。歡迎各位在評(píng)論里或者通過(guò)郵件(zhaiyifan56@gmail.com)補(bǔ)充其他點(diǎn),我會(huì)標(biāo)注出來(lái)源翩概。
歡迎加入QQ群:568863373啸澡。
歡迎關(guān)注我們的公眾號(hào):魔都三帥
,歡迎大家來(lái)投稿~只需要是未在微信平臺(tái)上發(fā)布過(guò)的技術(shù)相關(guān)類文章都可以哦(不局限于任何語(yǔ)言和平臺(tái))氮帐。