本文由云+社区发表

绝大多数程序只考虑了接口正常工作的场景,而用户在使用我们的产品时遇到的各类异常,全都丢在看似 ok 的 try catch 中。如果没有做好异常的兼容和兜底处理,会极大的影响用户体验,严重的还会带来安全和资损风险。

接口异常,通常可以分为以下三类:

  • 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 } = optionsreturn 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 状态码异常、接口劫持注入、慢网络、无网络状态等各种网络异常进行兼容处理。

欢迎留言分享你的代码实现

此文已由作者授权腾讯云+社区发布


99%的程序都没有考虑的网络异常相关推荐

  1. 微信小程序网络请求异常怎么办_微信小程序打开提示“网络异常,请检查网络状态”的解决方法...

    症状:打开微信小程序的时候,提示"网络异常,请检查网络状态",无法加载数据. 问题客户端:安卓手机 经过测试:IOS和微信桌面版,均正常. 唯独安卓手机不行,如下图所示,这个问题不 ...

  2. 好评率超高的几个硬核公众号,99%的程序员都关注了!

    是不是觉得公众号关注得太多,漫无目的地看文章,没有学到什么知识,还白白浪费了时间.诚然,随着移动互联网的饱和,以及抖音等短视频的崛起,移动互联网正式进入存量厮杀的阶段.伴随而来的则是信息的泛滥,以及优 ...

  3. 据说99%的程序猿都不懂得这样表白

    本文由"我的程序猿老公"小bu综合整理自 知乎 同名讨论帖. 那天突发奇想,想知道那些内向闷骚的单身程序猿们,是不是真的不会向妹子表达.就像某哥说的,我会写代码,但不会追妹子. 篮 ...

  4. 程序员分析:99%的创业公司都不值得加入

    程序员分析:99%的创业公司都不值得加入 https://blog.csdn.net/csdnsevenn/article/details/81073939 没人喜欢早请示晚汇报,我问过身边很多人,大 ...

  5. 程序员们要注意啦!99% 的创业公司都不值得加入!

    前天晚上,一位朋友约晚饭,开始比较诧异,因为这位朋友半年前刚从一家大公司离职,去了另一个城市的一家初创公司.跟朋友聊过,初创公司规模不大,十几号人,老板是传统行业的生意人,进入技术服务行业创业,off ...

  6. 【asp程序,服务器安全管理,网络联盟广告投放,邮件群发,其中任何一方面都可以,高手最佳】

    [asp程序,服务器安全管理,网络联盟广告投放,邮件群发,其中任何一方面都可以,高手最佳] 工作内容: 1.ASP程序:主要工作为公司程序模块开发以及网站程序维护. 2.服务器安全管理:为公司的服务器 ...

  7. 建议收藏99%的程序员都爱的网站

    导读 作为程序员,你每天接触到的比较多的网站或者平时比较喜欢浏览的网站有哪些?今天给大家介绍9个99%的程序员都爱的网站,建议收藏起来.如果你有什么其他有用的网站,也可以评论区推荐给大家. 1.Git ...

  8. iphone android传照片大小,iPhone竟然可以传文件到安卓机?99%的人都不知道

    原标题:iPhone竟然可以传文件到安卓机?99%的人都不知道 iPhone换了安卓手机, 旧iPhone手机上的数据该怎么办? 是全部按delete键(X)? 忘了一切,让数据随风? 还是一个个从旧 ...

  9. 程序员的价值观与网络的复杂性

    网络是极其复杂的,这种复杂包含混沌和不确定性,网络是一个典型的复杂系统.然而网络映射到程序员的心里,它只是一条确定的管道!这种思路会带来问题.程序员与运维/网管之间的斗争依然在继续,在这个无休止的争论 ...

最新文章

  1. [YTU]_2439( C++习题 复数类--重载运算符+)
  2. 一次php curl卡住的bug分析
  3. 买卖股票类问题动态规划解法(Leetcode题解-Python语言)
  4. [剑指offer]面试题第[63]题[Leetcode][第121题][JAVA][买卖股票的最佳时机][动态规划][暴力]
  5. 同步工具之CyclicBarrier循环栅栏
  6. Android NotificationManager详解
  7. Linux入门相关基础知识
  8. LeetCode之移除元素
  9. DirectX诊断工具怎么打开?怎么查看当前电脑所支持的DirectX版本?
  10. ES中 minimum_should_match 的用法和误区
  11. 用 Swift、Foursquare API 和 Realm 創建一個咖啡店 App
  12. 2018TLC大会精彩回顾
  13. 华为交换机常见ARP操作
  14. # 互动媒体期末作业——P5.js“画板”
  15. 网易云音乐用户微观洞察精细化运营
  16. iOS 多线程使用示例
  17. Cocos2d-x中图字原理之深入分析
  18. 以IM为例看58同城典型技术架构演变
  19. 计算机专业学渣面试,感觉读了计算机专业真的是一条不归路
  20. 移动端:M站和APP的区别

热门文章

  1. Flask源码解析:从第一个版本开始阅读Flask源码
  2. Java并发 乐观锁和悲观锁 乐观锁的一种实现方式CAS
  3. 论PHP框架设计模式及MVC的缺陷
  4. mysql 字符串函数
  5. 通俗易懂的rpc原理
  6. AVL平衡树的插入例程
  7. cen7布署mysql数据库
  8. SAS学习笔记之《SAS编程与数据挖掘商业案例》(3)变量操作、观测值操作、SAS数据集管理...
  9. 实现100以内的素数输出(Python与C++对比)
  10. bundler for jekyll