使用whistle模拟cgi接口异常:错误码、502、慢网速、超时
接口异常,通常可以分为以下三类:
-
CGI 逻辑出错。
-
服务不稳定。如服务器不稳定导致 nginx 各类 500、502,cgi 路径调整导致的 404
-
用户网络环境差。如,网络不稳定、网速慢、运营商劫持等
那么,我们在写代码时,如何快速的模拟这些接口异常,做好程序的兼容处理呢?
今天向大家介绍网络调试神器 whistle 的网络异常调试方法,如果你还没用过 whistle,请参考《8102 年的程序员不需要 Hosts 和 Fiddler》。
假设我们有以下前端页面 index.html,放置在自己的本地路径:
<p id="success" style="color:green;"></p> <p id="fail" style="color:red;"></p> <script> fetch(`/mock?r=${Math.random()}`) .then(response => { return response.json() }) .then(v => { document.getElementById('success').innerHTML = v.data; }).catch(err => { document.getElementById('fail').innerHTML = err.message; }) </script>
接下来,打开 whistle Rules 配置面板 http://127.0.0.1:8899/#rules ,配置模拟的 demo page 和 mock CGI:
*/mock file://({"code":0,"data":"success"}) # 配置 mock cgi 为模拟的 json 数据 example.com file:///Users/kaiye/Projects/Markdown/20181213/ # 配置任意域名到本地 demo 目录,这里注意替换成自己的路径
打开 http://example.com ,正常逻辑下页面展示出了绿色的 success ,现在我们开始加入一些网络异常。
1、业务逻辑异常处理
例如 CGI 没有返回 data
字段,而是返回了一个错误码 code
和对应的 message
,针对这种业务逻辑异常我们只需在第二个 then
中做好 code 值的判断即可(注意,这里的 code、message、data 只是示例,实际业务 CGI 中的 JSON 结构体的字段名很可能不同):
fetch(`/mock?r=${Math.random()}`) .then(response => response.json()) .then((v) => { // 业务逻辑异常处理 if (v.code !== 0) { return Promise.reject(new Error(`ERROR_LOGIC_CODE:${v.code}`)); } document.getElementById('success').innerHTML = v.data; }) .catch((err) => { document.getElementById('fail').innerHTML = err.message; });
相应的 whistle 配置如下:
*/mock file://({"code":12345,"message":"some_logic_error"}) # 模拟业务逻辑异常
2、服务器异常处理
如果服务器直接抛出了 502 错误码,我们希望代码能给用户提示的同时,再做一个异常上报。
fetch(`/mock?r=${Math.random()}`) .then((response) => { // 服务器异常处理 if (response.ok) { return response.json(); } return Promise.reject(new Error(`ERROR_STATUS_CODE:${response.status}`)); }) .then((v) => { // 业务逻辑异常处理 if (v.code !== 0) { return Promise.reject(new Error(`ERROR_LOGIC_CODE:${v.code}`)); } document.getElementById('success').innerHTML = v.data; }) .catch((err) => { const [type, value] = err.message.split(':'); // 异常类型上报 console.log(type, value); document.getElementById('fail').innerHTML = err.message; });
通过 whistle 的模拟配置如下:
*/mock statusCode://502 # 模拟 HTTP 状态码异常
3、接口被劫持注入
如果 CGI 被运营商劫持注入,可能导致接口返回一个不合法的 JSON 结构,最前面的 response.json()
会抛异常,我们可以提前 catch 住:
fetch(`/mock?r=${Math.random()}`).then((response) => { // 服务器异常处理 if (response.ok) { return ( response .json() // 接口数据解码异常处理 .catch(err => Promise.reject(new Error('ERROR_DECODE_JSON'))) ); } return Promise.reject(new Error(`ERROR_STATUS_CODE:${response.status}`)); });
whistle 模拟配置如下:
*/mock file://(<div>hijacking</div>{"code":0,"data":"success"}) # 模拟接口被劫持注入 1
借助 htmlAppend 和 values 配置,可以模拟更复杂的注入示例:
*/mock file://({"code":0,"data":"success"}) htmlAppend://{hijacking.html} # 模拟接口被劫持注入 2 ```hijacking.html <script> alert('hijacking') </script> ```
4、用户网络不稳定
如果我们要模拟请求发出 10 秒后断网或网络不通的情况,可以通过 whistle 这样配置:
*/mock reqDelay://10000 enable://abort # 模拟 10 秒超时后网络不通
让用户苦苦等待 10 秒,再报错的体验太糟糕。我们可以封装一个能配置超时时间的请求发送函数,同时把上面提到的错误异常都一起配置进来。
<p id="success" style="color:green;"></p> <p id="fail" style="color:red;"></p> <script> function myFetch(url, configOptions) { const options = Object.assign( { timeout: 3000 }, configOptions ) const { timeout } = options return new Promise((resolve, reject) => { // 超时异常处理 const timer = setTimeout(() => { reject(new Error(`ERROR_TIMEOUT:${timeout}`)) }, timeout) fetch(url, options) .then(data => { clearTimeout(timer) resolve(data) }) .catch(err => { clearTimeout(timer) reject(err) }) }) .then(response => { // 服务器异常处理 if (response.ok) { return ( response .json() // 接口数据解码异常处理 .catch(err => Promise.reject(new Error('ERROR_DECODE_JSON'))) ) } else { return Promise.reject( new Error(`ERROR_STATUS_CODE:${response.status}`) ) } }) .then(v => { // 业务逻辑异常处理 if (v.code !== 0) { return Promise.reject(new Error(`ERROR_LOGIC_CODE:${v.code}`)) } else { return v.data } }) .catch(err => { const [type, value] = err.message.split(':') // 异常类型上报 console.log(type, value) return Promise.reject(err) }) } myFetch(`/mock?r=${Math.random()}`) .then(data => { document.getElementById('success').innerHTML = data }) .catch(err => { document.getElementById('fail').innerHTML = err.message }) </script>
这样,自定义的 myFetch
只需关注业务具体逻辑,针对不同的 catch error 做对应的处理。
除以上提到的协议命令字外,whistle 还支持 resSpeed
用于模拟低网速传输(单位:kb/s),tpl
协议则可以根据请求传入参数来动态模拟不同的数据。在 Frames 面板,还可以对 WebSocket/Socket 请求进行暂停、延迟等网络异常的模拟。
小程序 fetch API 实现
最后,留一道思考题。
近来微信小程序开发非常火,小程序原生提供的 wx.request API 能用于发送 HTTPS 请求,请在它的基础之上进行封装,支持 promise 调用和 timeout
超时时间定义(小程序默认的请求超时定义在 app.json 中,不够灵活),并针对以上提到的 HTTP 状态码异常、接口劫持注入、慢网络、无网络状态等各种网络异常进行兼容处理。
欢迎留言分享你的代码实现,在公众号「猫哥学前班」中回复关键词 request
,可以参考我的实现和 whistle rules 配置。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
2015-12-18 猫哥网络编程系列:HTTP PEM 万能调试法