使用whistle模拟cgi接口异常:错误码、502、慢网速、超时
绝大多数程序只考虑了接口正常工作的场景,而用户在使用我们的产品时遇到的各类异常,全都丢在看似 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 } = 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 配置。

使用whistle模拟cgi接口异常:错误码、502、慢网速、超时的更多相关文章
- Redis Windows 服务启动异常 错误码1067
https://blog.csdn.net/after_you/article/details/62215163 Redis Windows 服务启动异常 错误码1067 下载了Redis 2.8.2 ...
- Spring/SpringBoot定义统一异常错误码返回
配置 大致说下流程, 首先我们自定义一个自己的异常类CustomException,继承RuntimeException.再写一个异常管理类ExceptionManager,用来抛出自定义的异常. 然 ...
- JavaWeb项目中获取对Oracle操作时抛出的异常错误码
最近在项目中碰到了这么一个需求,一个JavaWeb项目,数据库用的是Oracle.业务上有一个对一张表的操作功能,当时设置了两个字段联合的唯一约束.由于前断没有对重复字段的校验,需要在插入时如果碰到唯 ...
- MySQL 异常错误码使用 及 对照表
DECLARE CONTINUE HANDLER FOR SQLWARNING, SQLEXCEPTION, NOT FOUND BEGIN GET DIAGNOSTICS CONDITION cod ...
- Java异常封装(自己定义错误码和描述,附源码)
真正工作了才发现,Java里面的异常在真正工作中使用还是十分普遍的.什么时候该抛出什么异常,这个是必须知道的. 当然真正工作里面主动抛出的异常都是经过分装过的,自己可以定义错误码和异常描述. 下面小宝 ...
- Java异常封装(自定义错误码和描写叙述,附源代码)
真正工作了才发现.Java里面的异常在真正工作中使用还是十分普遍的. 什么时候该抛出什么异常,这个是必须知道的. 当然真正工作里面主动抛出的异常都是经过分装过的,自己能够定义错误码和异常描写叙述. 以 ...
- [2017-08-28]Abp系列——业务异常与错误码设计及提示语的本地化
本系列目录:Abp介绍和经验分享-目录 前言 ABP中有个异常UserFriendlyException经常被使用,但是它所在的命名空间是Abp.UI,总觉得和展现层联系过于紧密,在AppServic ...
- .NET中异常与错误码优劣势对比
.NET之所以选择异常,而不是返回错误码来报告异常,是由于前者有以下几个优势: 1.异常与oop语言的结合性更好.oop语言经常需要对成员签名强加限制,比如c#中的构造函数.操作符重载和属性,开发者对 ...
- 网络请求及各类错误代码含义总结(包含AFN错误码大全)
碰见一个很奇葩的问题, 某些手机在设置了不知什么后, 某些 APP 死活 HTTPS 请求失败, 例如以 UMeng 统计HTTP 请求失败为例, Log如下: UMLOG: (Error App ...
随机推荐
- SSM-MyBatis-03:Mybatis中简单的整合日志
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥-------------简单的整合日志,首先提供俩种方式,log4j和logback简单的说一下logback,他由log4j的原作者编 ...
- java.lang.OutOfMemoryError: PermGen space 解决办法
最近在学习spring mvc时,用了大量的第三方jar包,导致启动tomcat时报内存溢出的错 java.lang.OutOfMemoryError: PermGen space 解决办法:在mye ...
- Linux 上一些常用命令
切换权限: sudo chown -R 权限名: 文件 tar -zcvf 文件夹.tar 文件夹--exclude=要过滤的文件夹路径 重启crontab :service crond restar ...
- ASP.NET Core Identity Hands On(1)——Identity 初次体验
ASP.NET Core Identity是用于构建ASP.NET Core Web应用程序的成员资格系统,包括成员资格.登录和用户数据存储 这是来自于 ASP.NET Core Identity 仓 ...
- windows部署SpiderKeeper(爬虫监控)
最近发现了一个spdierkeeper的库,这个库的主要用途是在于配合这scrpyd管理你的爬虫,支持一键式部署,定时采集任务,启动,暂停等一系列的操作.简单来说将scrapyd的api进行封装,最大 ...
- []T 还是 []*T, 这是一个问题
全面分析Go语言中的类型和类型指针的抉择 目录 [−] 副本的创建 T的副本创建 *T的副本创建 如何选择 T 和 *T 什么时候发生副本创建 最常见的case map.slice和数组 for-ra ...
- bzoj 3551 kruskal重构树dfs序上的主席树
强制在线 kruskal重构树,每两点间的最大边权即为其lca的点权. 倍增找,dfs序对应区间搞主席树 #include<cstdio> #include<cstring> ...
- BZOJ_3282_Tree_LCT
BZOJ_3282_Tree_LCT Description 给定N个点以及每个点的权值,要你处理接下来的M个操作. 操作有4种.操作从0到3编号.点从1到N编号. 0:后接两个整数(x,y),代表询 ...
- BZOJ_2693_jzptab_莫比乌斯反演
BZOJ_2693_jzptab_莫比乌斯反演 Description Input 一个正整数T表示数据组数 接下来T行 每行两个正整数 表示N.M Output T行 每行一个整数 表示第i组数据的 ...
- Error【0002】:YUM本地源配置问题
1.1 问题背景 通过VMware workstation创建虚拟机,在虚拟机的CDROM设备中,装载操作系统镜像.然后通过mount -o loop的方式,将CDROM设备挂载到系统的/mnt/cd ...