注:本文內(nèi)容較長(zhǎng)且細(xì)節(jié)較多,建議先收藏再閱讀造成,原文將在 Github 上維護(hù)與更新址否。
在 HTTP 接口開(kāi)發(fā)與調(diào)試過(guò)程中贬派,我們經(jīng)常遇到以下類似的問(wèn)題:
- 為什么本地環(huán)境接口可以調(diào)用成功,但放到手機(jī)上就跑不起來(lái)?
- 這個(gè)接口很復(fù)雜琅绅,內(nèi)部調(diào)用了好幾個(gè)其他接口,如何定位問(wèn)題究竟出在哪一步浑吟?
- 后端開(kāi)發(fā)還沒(méi)有把接口提供好,前端開(kāi)發(fā)任務(wù)無(wú)法推進(jìn)……
「貓哥網(wǎng)絡(luò)編程系列」最核心的任務(wù)便是向各位分享一個(gè)我從多年的前后端項(xiàng)目中總結(jié)而來(lái)的「萬(wàn)能」HTTP 調(diào)試法耗溜,掌握并從網(wǎng)絡(luò)編程原理上理解它,能讓我們順利定位并解決所有 HTTP 接口問(wèn)題省容。由于該方法主要涉及到的知識(shí)點(diǎn)包括 HTTP 代理(Proxy)抖拴、編輯(Edit)與數(shù)據(jù)模擬(Mock),因此我稱之為「HTTP PEM 調(diào)試法」腥椒。
接下來(lái)阿宅,我們就針對(duì)前面提出的幾個(gè)問(wèn)題,詳細(xì)講解下 PEM 調(diào)試法的思路笼蛛。
如何調(diào)試線上 App 中的 H5 頁(yè)面洒放?
「HTTP PEM 調(diào)試法」之 Proxy
在上一期《貓哥網(wǎng)絡(luò)編程系列:詳解 BAT 面試題》中,我們有介紹到 Windows 下的 Fiddler 和 Mac 下的 Charles 這兩款 HTTP 抓包工具滨砍,其實(shí)它們就是兩個(gè) HTTP 代理服務(wù)器(HTTP Proxy Server)往湿。由于 HTTP 是一種符合 REST 架構(gòu)風(fēng)格(Representational State Transfer)的協(xié)議,具有無(wú)狀態(tài)(Stateless)與統(tǒng)一接口(Uniform Interface)的架構(gòu)約束惋戏,因此其代理機(jī)制的實(shí)現(xiàn)十分的簡(jiǎn)單领追。
打個(gè)比方,我們可以把 Proxy Server 理解成一個(gè)快遞中轉(zhuǎn)站响逢,當(dāng)一個(gè)包裹經(jīng)過(guò)中轉(zhuǎn)站時(shí)绒窑,包裹的信息(發(fā)件人、收件人與包裹里的貨物)通常不會(huì)做任何的改動(dòng)舔亭,直接發(fā)往下一個(gè)中轉(zhuǎn)站或顧客手中些膨。但中轉(zhuǎn)站完全有能力修改快遞單信息、拆箱檢查貨物钦铺,甚至是私吞或調(diào)換貨物订雾。
當(dāng)我們需要快速定位「線上產(chǎn)品的接口問(wèn)題」時(shí),如果沒(méi)有源碼职抡、數(shù)據(jù)葬燎、依賴服務(wù)和足夠的時(shí)間去搭建一個(gè)測(cè)試環(huán)境,則通常會(huì)使用 HTTP 代理服務(wù)器來(lái)進(jìn)行快速抓包調(diào)試。
Fiddler 默認(rèn)只允許本地 IP(127.0.0.1)使用代理服務(wù)谱净,通過(guò)設(shè)置「Tools -> Connections -> Allow remote computers to connect」可以開(kāi)啟其他 IP(通常是同一局域網(wǎng)內(nèi)的其他設(shè)備)使用代理服務(wù)窑邦。
Charles 默認(rèn)開(kāi)放代理服務(wù),但陌生設(shè)備首次連接時(shí)需要授權(quán)確認(rèn)壕探,通過(guò)以下配置可以設(shè)置成無(wú)需授權(quán)冈钦。
以上兩款軟件默認(rèn)的代理端口均是 8888 ,軟件開(kāi)啟之后李请,我們可以在對(duì)應(yīng)的平臺(tái)終端下通過(guò) ipconfig
(Windows) 或 ifconfig
(Mac)命令查詢本機(jī)的局域網(wǎng) IP瞧筛,還可以使用 telnet
命令檢查代理通道是否可用。(注:Win7 下如何開(kāi)啟 telnet 命令請(qǐng)參考百度經(jīng)驗(yàn)导盅。)
以下是 Windows 下 CMD 終端的使用截圖较幌,Mac 系統(tǒng)下請(qǐng)類比參考。
接下來(lái)白翻,我們將手機(jī)的 Wi-Fi 代理設(shè)置為上述的 IP 與 端口號(hào)乍炉,以下是 iOS 的設(shè)置截圖( Android 系統(tǒng)通常是長(zhǎng)按已連接的 Wi-Fi ,在彈出的高級(jí)設(shè)置菜單中配置代理服務(wù)器)滤馍。
至此岛琼,手機(jī)上任意應(yīng)用發(fā)起的 HTTP 請(qǐng)求都將會(huì)被代理服務(wù)器(本例中的 Fiddler/Charles 軟件)監(jiān)聽(tīng)到。
「HTTP PEM 調(diào)試法」之 Edit
通過(guò)代理服務(wù)器監(jiān)聽(tīng)到 HTTP 請(qǐng)求之后巢株,我們可以通過(guò)瀏覽報(bào)文的詳細(xì)信息槐瑞,定位出可能的接口問(wèn)題。Fiddler 與 Charles 都具有同樣強(qiáng)大的 HTTP 編輯(Edit)阁苞、重發(fā)(Replay/Repeat)困檩、斷點(diǎn)(Breakpoints)功能。Charles 的基礎(chǔ)與高級(jí)用法請(qǐng)參考《Charles 從入門到精通》猬错,F(xiàn)iddler 教程可以參考 OSChina 專題《HTTP調(diào)試代理 Fiddler》窗看,以下介紹 Fiddler 的部分常見(jiàn)用法。
Fiddler Edit 與 AutoResponder
抓到手機(jī) HTTP 請(qǐng)求之后倦炒,通過(guò)編輯(Unlock For Editing)和重發(fā)(Replay)操作可以不斷地調(diào)試接口的響應(yīng)是否符合預(yù)期显沈。
通過(guò)設(shè)置自動(dòng)響應(yīng)規(guī)則(AutoResponder Rules)可以將響應(yīng)頭設(shè)置成常見(jiàn)狀態(tài)碼的返回,或?qū)㈨憫?yīng)體映射成本地文件逢唤,通過(guò)外部編輯器修改文件內(nèi)容進(jìn)行調(diào)試拉讯。其中,若設(shè)置響應(yīng)為 *bpu
或 *bpafter
可以在請(qǐng)求前與響應(yīng)前的事件觸發(fā)時(shí)進(jìn)行斷點(diǎn)調(diào)試鳖藕,十分方便魔慷。
需要注意的是,在 Fiddler 中使用 Replay 功能重發(fā)請(qǐng)求時(shí)著恩,請(qǐng)求由 Fiddler 代理重新發(fā)起而非手機(jī)院尔,因此手機(jī) App 中的 H5 不會(huì)有任何變化蜻展。只有重新刷新 App 的 H5 頁(yè)面,配合 HTTP 斷點(diǎn)調(diào)試(Breakpoints )的方式才可以讓修改后的 HTTP 響應(yīng)體在 App中生效邀摆。這里介紹另外一種配合 Weinre 的調(diào)試用法纵顾。
Weinre 基本用法
Weinre 屬于知名 Hybrid 框架 Cordova 中的一款 Web App 遠(yuǎn)程調(diào)試工具。通過(guò)在頁(yè)面中注入一段 JS 腳本栋盹,可以在 PC 和手機(jī)端的 H5 頁(yè)面之間建立一個(gè) Socket 雙向數(shù)據(jù)傳輸通道施逾。原理上可以理解為,當(dāng)我們?cè)?PC 端的后臺(tái)進(jìn)行 debug 時(shí)例获,相關(guān)的操作被序列化成一組 JSON 字符串汉额,數(shù)據(jù)經(jīng)由通道傳輸給手機(jī)端中的 H5 頁(yè)面,頁(yè)面在接收到這些數(shù)據(jù)之后反序列化成相應(yīng)的 JS 腳本操作榨汤,在其 window 上下文中執(zhí)行蠕搜,并將執(zhí)行的結(jié)果回傳給通道,PC 端的 Chrome 通過(guò)監(jiān)聽(tīng)通道獲取到相應(yīng)的數(shù)據(jù)在 debug 后臺(tái)中展現(xiàn)出來(lái)收壕。
以下介紹 Weinre 的基本用法:
- 通過(guò) npm 全局安裝 weinre:
npm install -g weinre
- 在本地 8081 端口上啟動(dòng) weinre 服務(wù):
weinre --boundHost 0.0.0.0 --httpPort 8081
讥脐。通常在 Node.js 的服務(wù)中綁定 IP 為 0.0.0.0 而非 127.0.0.1(本地 IP),意味著可以讓任意來(lái)源的 IP 訪問(wèn)該服務(wù) - 通過(guò)上文介紹的
ipconfig
(Mac 為ifconfig
)命令獲取本機(jī) IP 后啼器,在本機(jī) Chrome 瀏覽器中訪問(wèn) Weinre 管理后臺(tái):http://10.2.69.47:8081 (本例中我的 IP 為 10.2.69.47,請(qǐng)注意將其替換成自己的局域網(wǎng) IP) - 在管理后臺(tái)我們能看到相關(guān)使用說(shuō)明俱萍,要求將以下腳本插入需要調(diào)試的 H5 頁(yè)面中:
<script src="http://10.2.69.47:8081/target/target-script-min.js#anonymous"></script>
- 將以上腳本插入進(jìn) H5 頁(yè)面后端壳,我們?cè)?PC 端 Chrome 中,通過(guò) http://10.2.69.47:8081/client/#anonymous 后臺(tái)點(diǎn)擊進(jìn)入相應(yīng)的客戶端調(diào)試界面
問(wèn)題是枪蘑,我們「如何將 Weinre Script 自動(dòng)注入到手機(jī)的 H5 頁(yè)面中」损谦?
HTTP Script 注入
想必用過(guò)中國(guó)電信寬帶的同學(xué)都有過(guò)這樣的體驗(yàn):在剛開(kāi)始瀏覽網(wǎng)頁(yè)時(shí),會(huì)自動(dòng)跳出一些「寬帶升級(jí)優(yōu)惠」岳颇、「寬帶繳費(fèi)提醒」之類的頁(yè)面照捡。這種耍流氓的方式便是寬帶運(yùn)營(yíng)商在 HTTP 代理層面的 Script 注入行為。前面已經(jīng)提到 HTTP 協(xié)議是一種 REST 風(fēng)格的架構(gòu)话侧,并且他的頭部與主體報(bào)文為字符串文本流(對(duì)比二機(jī)制栗精、十六進(jìn)制數(shù)據(jù)流),在不使用 HTTPS 的情況下瞻鹏,很容易被中間路由或代理網(wǎng)關(guān)進(jìn)行消息篡改悲立。
通過(guò) Fiddler Script 特性,我們可以自動(dòng)對(duì)經(jīng)過(guò) Fiddler 的 HTTP 流量進(jìn)行二次修改新博,注入任意內(nèi)容(Mac 用戶若已了解相關(guān)知識(shí)點(diǎn)薪夕,請(qǐng)直接跳至下方的 Charles 截圖)。
打開(kāi) Fiddler 菜單「Rules -> Customize Rules… 」赫悄,如果是首次開(kāi)啟會(huì)要求先下載安裝 Fiddler ScriptEditor原献。打開(kāi) Fiddler ScriptEditor 之后馏慨,找到以下代碼塊(或使用菜單「Go -> to OnBeforeResponse」):
static function OnBeforeResponse(oSession: Session) {
if (m_Hide304s && oSession.responseCode == 304) {
oSession["ui-hide"] = "true";
}
}
Fiddler Script 使用的編程語(yǔ)言是 JScript.NET(JavaScript 和 C# 的混合語(yǔ)法,類似 TypeScript)姑隅,OnBeforeResponse
是 HTTP Response 響應(yīng)前的事件函數(shù)写隶,我們只需要在這里判斷「如果開(kāi)啟了 Weinre Debug 功能,那么就在所有的 HTML 響應(yīng)體中注入 Weinre Script」粤策,以下是我修改的示例代碼樟澜,覆蓋以上代碼塊即可。
public static RulesOption("Enable Weinre Script")
var m_EnableWeinreScript: boolean = true;
public static var g_weinreScriptString: String = '<script src="http://127.0.0.1:8080/target/target-script-min.js#anonymous"></script>';
public static ToolsAction("Config Weinre Script")
function ConfigWeinreScript(){
g_weinreScriptString = FiddlerObject.prompt("Text beblow will inject into HTML pages when 'Enable Weinre Script' rule is Enabled.", g_weinreScriptString , "Please Input the Weinre Script");
}
static function OnBeforeResponse(oSession: Session) {
if (m_Hide304s && oSession.responseCode == 304) {
oSession["ui-hide"] = "true";
}
if (m_EnableWeinreScript && oSession.oResponse.headers.ExistsAndContains("Content-Type","text/html")){
oSession.utilDecodeResponse();
if(oSession.utilFindInResponse("</html>", false)>-1){
oSession["ui-backcolor"] = "#5E30B5";
oSession["ui-color"] = "white";
oSession.utilReplaceRegexInResponse("<\/html>", g_weinreScriptString + '</html>');
}
}
}
修改保存后重啟 Fiddler(或使用菜單「Tools -> Reset Script」)以生效規(guī)則叮盘,接下來(lái)運(yùn)行「Tools」菜單中新出現(xiàn)的「Config Weinre Script」秩贰,將 127.0.0.1:8080 替換成自己本機(jī)的局域網(wǎng) IP 與 weinre 服務(wù)端口號(hào),同時(shí)開(kāi)啟菜單「Rules -> Enable Weinre Script」柔吼。至此毒费,所有 HTML 頁(yè)面將會(huì)被自動(dòng)注入 Weinre Script,之后我們就可以在 weinre 后臺(tái)中開(kāi)始調(diào)試相關(guān)頁(yè)面愈魏。以下是參考截圖:
可以看到 HTTP 響應(yīng)體中已經(jīng)被動(dòng)態(tài)注入 Weinre Script觅玻。
在 Mac Charles 下的 Script 注入配置更加容易,只需利用其 「Rewrite」功能進(jìn)行簡(jiǎn)單的配置即可培漏,參看下圖:
通過(guò) Fiddler/Charles 代理工具將 JS 腳本注入成功后溪厘,我們便可以通過(guò)前文提到的 weinre 后臺(tái)開(kāi)始 debug 相應(yīng)的頁(yè)面,以下是在 iPhone 模擬器中調(diào)試新浪微博界面的截圖:
使用該方法可以調(diào)試 Android 和 iOS 中「任意 App 的 H5 頁(yè)面」牌柄,但由于主要使用了 weinre 服務(wù)畸悬,其原理決定了該方法無(wú)法像真正的 Chrome DevTools 一樣支持 JS 斷點(diǎn)調(diào)試、Profiles 性能分析等功能珊佣,具有一定的局限性蹋宦。在實(shí)際 Web App 開(kāi)發(fā)過(guò)程中,推薦使用以下工具進(jìn)行調(diào)試 :
- 微信官方調(diào)試工具 調(diào)試基于微信的 Web App
- Chrome Remote Debugging 調(diào)試 Android Web App
- Safari Remote Debugging 調(diào)試 iOS Web App
由此可見(jiàn)咒锻,「HTTP PEM 調(diào)試法」是一個(gè)通用的 HTTP 接口調(diào)試方案冷冗,可以用來(lái)快速定位線上接口問(wèn)題,對(duì)于開(kāi)發(fā)人員來(lái)說(shuō)掌握其背后的 HTTP 協(xié)議及其代理機(jī)制的原理更加重要惑艇,接下來(lái)我們聊聊常見(jiàn)的 HTTP 接口開(kāi)發(fā)協(xié)作方法與 Mock 思路蒿辙。
我的開(kāi)發(fā)任務(wù)沒(méi)法推進(jìn),因?yàn)槟衬车慕涌谶€沒(méi)提供給我敦捧。
「HTTP PEM 調(diào)試法」之 Mock
希望新手程序員在看完這一章節(jié)之后须板,不要再向你的項(xiàng)目組和上級(jí)反饋這樣的說(shuō)法,因?yàn)?HTTP Mock(接口數(shù)據(jù)模擬)是一項(xiàng)網(wǎng)絡(luò)編程的基礎(chǔ)技能兢卵,從實(shí)際項(xiàng)目經(jīng)驗(yàn)來(lái)看习瑰,大部分基于 HTTP 接口的任務(wù)都可以并行開(kāi)發(fā)。
最簡(jiǎn) HTTP API
不同崗位(例如前端開(kāi)發(fā)與后臺(tái)開(kāi)發(fā))或不同業(yè)務(wù)(例如訂單系統(tǒng)與賬戶系統(tǒng))的開(kāi)發(fā)人員開(kāi)始并行開(kāi)發(fā)任務(wù)之前秽荤,首先要做的應(yīng)該是對(duì)耦合和相互依賴的任務(wù)進(jìn)行邊界劃分與規(guī)則約定甜奄。具體到某個(gè) HTTP API 接口的約定上柠横,至少應(yīng)該明確以下信息:
- 是否按照 RESTful API 的約定來(lái)設(shè)計(jì)接口
- 接口的路徑、提交方法课兄、參數(shù)牍氛、編碼類型(Enctype/Content-Type)
- 接口返回的錯(cuò)誤碼(code)、消息說(shuō)明(message)烟阐、業(yè)務(wù)數(shù)據(jù)(data)
針對(duì)以上三條信息搬俊,我設(shè)想的「最簡(jiǎn)」 HTTP API 包含以下?幾條原則,供各位參考:
1蜒茄、不使用 RESTful API 來(lái)設(shè)計(jì)接口
RESTful API 實(shí)際上是利用 HTTP 協(xié)議的語(yǔ)義(提交類型唉擂、返回碼、Hypermedia Link)來(lái)將所有接口操作抽象化為一系列資源對(duì)象檀葛。這要求 API 的設(shè)計(jì)者與調(diào)用者都具備深厚的 HTTP 協(xié)議功底玩祟、語(yǔ)義化與抽象化能力。
- RESTful 作為一個(gè) Buzzword(流行詞)屿聋,其含義已經(jīng)被曲解空扎。HTTP 協(xié)議和 REST 架構(gòu)的設(shè)計(jì)者 Roy Fielding 很反感這一點(diǎn),還專門開(kāi)了博客以正視聽(tīng)润讥。大多數(shù)人只將 HTTP 當(dāng)做一種傳輸協(xié)議來(lái)使用(既成事實(shí))转锈,并不能真正理解 REST 架構(gòu)風(fēng)格;
- RESTful API 將所有請(qǐng)求抽象化為資源名詞(Resources)的做法爭(zhēng)議很大楚殿。這種做法總會(huì)讓我回想起上個(gè)世紀(jì)用 FrontPage 做網(wǎng)頁(yè)的經(jīng)歷黑忱,「設(shè)置一個(gè)超鏈接,從某個(gè)資源跳到另外一個(gè)資源」勒魔。在經(jīng)過(guò) Web 2.0 浪潮,進(jìn)入移動(dòng)互聯(lián)網(wǎng)時(shí)代后菇曲,這種 API 設(shè)計(jì)容易給人帶來(lái)困惑冠绢。例如「登錄、注冊(cè)」這樣的「動(dòng)詞」如何抽象成「名詞」(還好有 Github API 可以參考 )常潮。而刻意的使用 「HTTP CRUD」(POST/GET/PUT/DELETE Method)操作「資源化」之后的接口弟胀,并未帶來(lái)更多實(shí)質(zhì)上的收益;
- HTTP 狀態(tài)碼的分層思路在 RESTful API 模式下被破壞了喊式。HTTP 1.0 中定義的常見(jiàn)狀態(tài)碼已經(jīng)足夠網(wǎng)絡(luò)中間組件(代理孵户、網(wǎng)關(guān)、路由)使用岔留,HTTP 1.1 中加入?的很多狀態(tài)碼缺乏實(shí)際場(chǎng)景(例如 306 狀態(tài)碼的廢棄)夏哭,它們?cè)黾恿酥虚g組件以及瀏覽器對(duì)規(guī)范理解與實(shí)現(xiàn)的要求。盡可能的將狀態(tài)碼交給相應(yīng)的接口邏輯層而非 HTTP 協(xié)議層献联,能夠?qū)?wèn)題簡(jiǎn)化竖配;
- 對(duì)比以英文為母語(yǔ)的國(guó)外開(kāi)發(fā)者而言何址,國(guó)內(nèi)開(kāi)發(fā)者對(duì)語(yǔ)義化的認(rèn)知難度更高,例如 RESTful 建議資源命名用復(fù)數(shù)形式进胯,那收貨地址單詞 address 的復(fù)數(shù)形式是什么用爪?address or addresses ?address-list or address-lists胁镐?(沒(méi)過(guò)英語(yǔ)八級(jí)的同學(xué)已經(jīng)哭暈在廁所 T_T)
- 每個(gè)人對(duì) RESTful API 的理解都不同偎血,在 HTTP 協(xié)議層面做擴(kuò)展與實(shí)現(xiàn),不如交給接口設(shè)計(jì)者與調(diào)用者自己來(lái)約定數(shù)據(jù)結(jié)構(gòu)(或者參考 JSON-RPC 規(guī)范)盯漂。把 HTTP 只當(dāng)做傳輸協(xié)議來(lái)使用的好處是颇玷,當(dāng)后端服務(wù)間的接口需要直接基于 TCP 傳輸層來(lái)做性能優(yōu)化時(shí),可以十分方便的切換成 Socket 的實(shí)現(xiàn)(之前在騰訊做微博相關(guān)項(xiàng)目時(shí)宠能,微博開(kāi)放平臺(tái)對(duì)外只提供 HTTP 的 Open API亚隙,但對(duì)內(nèi)可以提供更高頻率與頻次調(diào)用的原生 Socket 協(xié)議)。
2违崇、只使用 GET/POST Method
由于 HTTP 1.0 尤其是 HTML 的規(guī)范與應(yīng)用已經(jīng)深入人心阿弃。大部分開(kāi)發(fā)者能夠很自然的這樣理解:「GET」 表示「讀」操作,「POST」 表示「寫」操作羞延。這樣既可以保證中間組件與瀏覽器很好的利用 GET 的緩存機(jī)制渣淳,又能降低接口設(shè)計(jì)的復(fù)雜度。HTTP 之父 Roy Fielding 也說(shuō)過(guò)「It is okay to use POST」:
Some people think that REST suggests not to use POST for updates. Search my dissertation and you won’t find any mention of CRUD or POST. (很多人認(rèn)為 RESTful 建議不要使用 POST 用于提交更新伴箩,去翻一翻我的論文入愧,壓根就沒(méi)提到過(guò) POST 和其他「增查改刪」方面的內(nèi)容。)
但使用 POST 方法時(shí)尤其要注意:「使用統(tǒng)一的 Content-Type」嗤谚。這是一個(gè)容易被新手忽略的細(xì)節(jié)棺蛛,也是接口設(shè)計(jì)中經(jīng)常出錯(cuò)的點(diǎn)。在上一期的《貓哥網(wǎng)絡(luò)編程系列:詳解 BAT 面試題》中有問(wèn)到:
一個(gè) POST 請(qǐng)求的 Content-Type 有多少種巩步,傳輸?shù)臄?shù)據(jù)格式有何區(qū)別旁赊?
以下舉例一些常見(jiàn)類型的 HTTP POST Request 報(bào)文,請(qǐng)注意其中的 Content-Type
與 Body 的對(duì)應(yīng)關(guān)系(已手動(dòng)刪除無(wú)關(guān) HTTP Header)
POST /test.php HTTP/1.1
Host: 127.0.0.1:8080
Content-Length: 54
Content-Type: application/json
{"weixin_id":"imgXQB","weixin_name":"貓哥學(xué)前班"}
POST /test.php HTTP/1.1
Host: 127.0.0.1:8080
Content-Length: 74
Content-Type: application/x-www-form-urlencoded
weixin_id=imgXQB&weixin_name=%E7%8C%AB%E5%93%A5%E5%AD%A6%E5%89%8D%E7%8F%AD
POST /test.php HTTP/1.1
Host: 127.0.0.1:8080
Content-Length: 259
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryl60ti7CVoBj2kxfX
------WebKitFormBoundaryl60ti7CVoBj2kxfX
Content-Disposition: form-data; name="weixin_id"
imgXQB
------WebKitFormBoundaryl60ti7CVoBj2kxfX
Content-Disposition: form-data; name="weixin_name"
貓哥學(xué)前班
------WebKitFormBoundaryl60ti7CVoBj2kxfX--
只有客戶端 POST 請(qǐng)求體的消息格式與其請(qǐng)求頭聲明的 Content-Type 一致時(shí)椅野,服務(wù)端才能正確的接收與響應(yīng)终畅。因?yàn)樵S多后端的 Web 應(yīng)用框架會(huì)遵照 HTTP 協(xié)議的內(nèi)容協(xié)商原則(Content Negotiation)對(duì)響應(yīng)體進(jìn)行預(yù)處理,以提升開(kāi)發(fā)體驗(yàn)竟闪。例如离福,Python 的 Flask 框架 封裝了 request.json、request.form炼蛤、request.data 等一系列屬性用于存放不同類型的來(lái)源數(shù)據(jù)妖爷。
3、接口 URI 與參數(shù)命名風(fēng)格的一致性
- API URI 應(yīng)該全小寫理朋。屏蔽掉 Linux/Windows 操作系統(tǒng)對(duì)文件名大小寫敏感度不一致的問(wèn)題赠涮;
- URI 命名上應(yīng)該使用連字符「-」來(lái)間隔子寓,而不是使用下劃線「_」或駝峰式。這是出于視覺(jué)美觀度和英文語(yǔ)義方面的考慮笋除,英文域名規(guī)范規(guī)定可以使用連字符斜友,但不能使用下劃線,API 路徑應(yīng)該和 Domain 命名風(fēng)格一致垃它;
- URI 使用「動(dòng)詞+名詞」或者「名詞+動(dòng)詞」均可鲜屏,但選定一種之后應(yīng)該保持一致。接口風(fēng)格的一致性国拇,可以降低使用者的理解成本洛史,好的 API 命名風(fēng)格能讓人「以一知萬(wàn)」,能從一個(gè) API 猜測(cè)出所有其他 API 的命名形式酱吝;
- 參數(shù)命名上應(yīng)該使用下劃線「_」而非連接符「-」也殖。這點(diǎn)主要是從數(shù)據(jù)庫(kù)字段設(shè)計(jì)的統(tǒng)一性和后臺(tái)應(yīng)用程序框架的易用性來(lái)考慮;
- 不同接口的相同參數(shù)命名應(yīng)保持統(tǒng)一务热,并考慮擴(kuò)展要求忆嗜。例如,收集用戶信息的參數(shù)可以統(tǒng)一叫「ua」崎岂,為了便于擴(kuò)展可以約定將客戶端分辨率捆毫、瀏覽器型號(hào)等信息使用「||」字符串連接,如
ua=1280x768||chrome
冲甘,當(dāng)需要添加操作系統(tǒng)字段時(shí)绩卤,客戶端只需按規(guī)則追加信息到原來(lái)的參數(shù)上,如ua=1280x768||chrome||windows
江醇。該條原則還有許多其他的方法來(lái)實(shí)現(xiàn)濒憋,不再一一舉例。
4陶夜、返回?cái)?shù)據(jù)結(jié)構(gòu)的一致性
基本的返回體結(jié)構(gòu)跋炕,可參考以下示例代碼。
{
"code": "0",
"message": "success",
"data": {
"id" : "1",
"list" : []
}
}
寥寥的幾行代碼飽含了幾部深刻的血淚史:
- 出于一致性的考慮律适,
code
表示返回碼(也可以理解成錯(cuò)誤碼),成功時(shí)返回"0"
遏插,出錯(cuò)時(shí)按預(yù)設(shè)的錯(cuò)誤碼規(guī)則返回(微信的返回碼規(guī)范設(shè)計(jì)的并不好捂贿,因?yàn)闆](méi)有內(nèi)建的規(guī)律和語(yǔ)義); - 同上胳嘲,可以理解
message
與data
的設(shè)計(jì)厂僧。需要注意的是 data 只具有 Object 一種類型。無(wú)數(shù)據(jù)的時(shí)候返回一個(gè)空對(duì)象{}
(而非null
)了牛,有多條數(shù)據(jù)的時(shí)候?qū)? Array 類型數(shù)據(jù)放在其內(nèi)部的list
之類的屬性中颜屠; - 所有原始數(shù)據(jù)類型建議統(tǒng)一使用字符串類型辰妙,包括布爾值用
"0"
和"1"
。原因是前后端對(duì)浮點(diǎn)數(shù)運(yùn)算精度不一致甫窟,會(huì)導(dǎo)致商品價(jià)格的計(jì)算與展示出錯(cuò)密浑;iOS/Android 客戶端對(duì) JSON null、布爾類型轉(zhuǎn)換的不一致會(huì)導(dǎo)致頻繁的 App Crash粗井。
當(dāng)然尔破,也有許多其他的方案可以解決上面提到的問(wèn)題,但出于「最簡(jiǎn)」的原則浇衬,這樣約定的理解成本最低懒构。
最簡(jiǎn) Mock Server
有了最簡(jiǎn) API 的約定之后,實(shí)現(xiàn)最簡(jiǎn) Mock Server 就相對(duì)簡(jiǎn)單多了耘擂。
1胆剧、編寫返回的模擬數(shù)據(jù)
首先,我們按照 API 接口約定來(lái)新建一些模擬數(shù)據(jù)文件醉冤。例如新建一個(gè) 「mock-data.json」 的文件秩霍,將以上返回體數(shù)據(jù)保存其中。
2冤灾、運(yùn)行 php 內(nèi)置服務(wù)器
在命令行模式下運(yùn)行 php
命令前域,Mac 用戶直接打開(kāi)終端即可,Windows 用戶需要先安裝 XAMPP 套件韵吨,并將 php.exe 所在的目錄配置到系統(tǒng)環(huán)境變量中匿垄,再使用 CMD 運(yùn)行以下命令:
php -S 0.0.0.0:8080 mock-data.json
開(kāi)啟之后訪問(wèn)任意 API 地址(http://127.0.0.1:8080/any-api-uri-you-want/)均會(huì)返回 mock-data.json 的數(shù)據(jù)響應(yīng)體。通過(guò)將 8080 端口換成 80 端口(Mac 需要使用 sudo 權(quán)限)归粉,再設(shè)置類似 127.0.0.1 www.example.com
的 HOST 配置椿疗,便可以模擬 API 的 Domain Host(http://www.example.com/any-api-uri-you-want/)形式。
當(dāng)然糠悼,也可以自己編寫一個(gè) index.php 的入口文件來(lái)實(shí)現(xiàn)一個(gè)基于 URL Path 規(guī)則的簡(jiǎn)單 Rewrite 功能届榄,用來(lái)同時(shí)支持多個(gè) API 的數(shù)據(jù)模擬。
使用 Fiddler/Charles 的 Map Local 功能
Fiddler/Charles 的 Map Local(本地映射)不光是用于 HTTP Edit倔喂,同樣可以用于 HTTP Mock铝条,當(dāng)一個(gè) 404 請(qǐng)求(還未真正實(shí)現(xiàn)的 API)被代理服務(wù)器捕獲后,可以設(shè)置映射到本地自定義的 mock-data.json 模擬數(shù)據(jù)文件席噩,從而被模擬成一個(gè)正常的 200 請(qǐng)求班缰。
自動(dòng)化 Mock System 構(gòu)想
迄今為止,我還未發(fā)現(xiàn)一個(gè)理想中的 Mock API 開(kāi)源系統(tǒng)悼枢,如有哪位同學(xué)有見(jiàn)到過(guò)請(qǐng)?jiān)?Github 上留言周知埠忘,以下是我對(duì)最理想 Mock System 的構(gòu)想:
- API 錄入后臺(tái)。包含一個(gè)按項(xiàng)目(一般是 Domain)維度進(jìn)行 API 管理的后臺(tái)∮ǘ剩可以在后臺(tái)上錄入「請(qǐng)求 URI名船、參數(shù)、多種業(yè)務(wù)數(shù)據(jù)響應(yīng)體旨怠、全局錯(cuò)誤碼渠驼、API 錯(cuò)誤碼」等接口信息;
- API 接口文檔运吓。能夠基于 API 后臺(tái)數(shù)據(jù)渴邦,生成在線的 API 文檔平臺(tái);
- Postman 導(dǎo)入/導(dǎo)出拘哨。能夠基于 API 數(shù)據(jù)導(dǎo)出生成 Postman Collections谋梭,以便導(dǎo)入 Postman 中進(jìn)行 API 調(diào)試;
- Mock Server倦青。能夠基于 API 數(shù)據(jù)快速搭建類似 MockServer 的本地服務(wù)瓮床,或提供遠(yuǎn)程模擬接口服務(wù)。
「HTTP PEM」系統(tǒng)分析利器
這個(gè)接口很復(fù)雜产镐,內(nèi)部調(diào)用了好幾個(gè)其他接口隘庄,如何定位問(wèn)題究竟出在哪一步?
對(duì)于新人來(lái)說(shuō)癣亚,最快的成長(zhǎng)方式是不斷地在新項(xiàng)目中實(shí)踐丑掺,從頭到尾參與到項(xiàng)目的每個(gè)系統(tǒng)細(xì)節(jié)的設(shè)計(jì)與討論。如果能參與到重點(diǎn)述雾、大型項(xiàng)目中街州,甚至幸運(yùn)地得到大牛的親自指導(dǎo),成長(zhǎng)速度將會(huì)突飛猛進(jìn)玻孟。
但更多的情況是唆缴,新人作為離職程序員的補(bǔ)充力量來(lái)接手一個(gè)老項(xiàng)目甚至是爛攤子。面對(duì)一個(gè)復(fù)雜的陌生系統(tǒng)黍翎,吐槽與抱怨無(wú)濟(jì)于事面徽。這時(shí),如果能使用「HTTP PEM 調(diào)試法」匣掸,從接口設(shè)計(jì)與調(diào)用的角度來(lái)剖析趟紊、理解整個(gè)系統(tǒng)的設(shè)計(jì),就能快速上手業(yè)務(wù)碰酝。例如霎匈,PHP 程序員可以在項(xiàng)目代碼中所有的 curl 調(diào)用點(diǎn),將「CURLOPT_PROXY」設(shè)置成 Fiddler/Charles 的代理服務(wù)砰粹,然后一步步調(diào)試,從接口字段上理解數(shù)據(jù)庫(kù)設(shè)計(jì)和 Controller 背后的業(yè)務(wù)邏輯。
最后碱璃,歡迎各位給我留言分享更多關(guān)于「HTTP PEM」和其他調(diào)試方法的經(jīng)驗(yàn)與體會(huì)弄痹。