8
头图

前言

欢迎关注同名公众号《熊的猫》,文章会同步更新,也可快速加入前端交流群!

周一一大早就接到了测试的 Bug 反馈,其实也是用户的反馈:

【测试】移动端偶尔会出现 操作异常,请刷新重试! 的提示,但是重新刷新又好了,用户说好不容易填了数据一刷新就白费功夫了,而且在测试环境也能复现,不过目前不是那么好复现了,这是之前复现时的截图,你先看看

image.png

【卑微前端】毕竟没得选)好,我看看!

1.png

image.png

这是之前(被迫)接手的一个 React 老项目,其中的 请求 是通过自定义封装 XMLHttpRequest 的方式来实现的,没有使用 React CLI 的方式,而是自己基于 webpack 构建工具搭建的一个项目。

忠告】对待老项目核心就是要足够尊重,千万不要吐槽它(老项目很小气的),
04DCD231.png 一不小心就能给你 惊喜,还可能是 无限的惊喜 !!!

大致的复现步骤,如下

就是在 A 页面B 页面 间来回进行 上一步下一步 操作,然后再就会出现请求异常的情况。

有了以上信息,我们就可以开始 BUG 的探索之路,下面就逐步进行分析,欢迎一起探讨!

无需入侵代码的调试方式

为了更好的复现问题和调试 H5 移动端,我们通常会使用一些 移动网页前端开发工具,例如 vconsole,但是这意味着我们还得去修改 代码内容项目依赖,而且 vconsole 的交互也不太友好(懂的都懂),那么除此之外还有没有什么其他办法可以实现 无入侵的便捷调试 呢?

0BDBE842.gif

那自然是有的了,最常用的就是 抓包(如 whistle,详细用法可见 Whistle 帮助文档)、Chrome DevTools 远程调试 两种方式,这里也简单介绍一下它们的使用方式。

Chrome DevTools 远程调试手机端

这里的远程调试手机端用 Android 系统来演示,IOS 系统步骤是一致的,只是会有些细节差异,自行查找一下即可。

连接 手机 和 电脑

只需要使用 数据线手机端电脑端 连接起来,同时将手机中的 开发人员选项 中的 USB调试 选项开启,通常插入 USB 数据线后都会自动弹出选项框来让你进行选择 USB调试 模式,但部分手机的弹窗中可能没有和 USB调试 相关的内容,如下:

1685538061265(1).png

此时你可以点击手机 设置,然后使用其搜索功能快速搜索定位,如下:

1685538141650(1).png

1685538178295.png

访问 Chrome DevTools 远程调试界面

通过 chrome://inspect/#devices 访问调试界面,然后你就可以使用 微信访问目标页面 或者 手机浏览器访问目标页面,接着等待调试界面加载到对应的信息后,就可以点击对应的 inspect 进行调试了,具体如下:

image.png

whistle 抓包

这里的抓包用 Android 系统来演示,IOS 系统步骤是一致的,只是会有些细节差异,自行查找一下即可。

一键安装 Whistle(Mac 和 Windows)

npm i -g whistle && w2 start --init 一行命令完成如下操作:

  • 安装 Whistle
  • 启动 Whistle

    • 设置系统代理,或不代理的白名单域名
    • 安装根证书

访问 Whistle 界面

启动成功后,会在终端输出可访问的地址,默认为如下三个:

1685539858278.jpg

移动端安装 CA 证书

whistle 界面点击 HTTPS 选项,在手机端扫码并下载对应的 CA 证书,目的是在代理时能够正常访问 https 协议头 的站点,而且现在的站点大多都是使用 https 协议,因此最好操作这一步。

image.png

手机端下载 CA 证书 完毕后,就需要 授权安装手动安装,可以在手机 设置 中快速搜索查找,然后按指示安装最近下载文件即可:

1685540376530.png

1685540431632.png

保证手机端和电脑端处于同一网段

也就是保证 手机端 连接的 WIFI 要和 电脑端 正在使用的网络是一致的即可。

设置手机端网络代理

保证是同一网段后,就需要设置代理了:

  • 找到当前连接的 WIFI,长按然后修改网络

    1685541012389(1).png

  • 配置代理信息

    • 服务器主机名 就是填写你的电脑的 IP,当成功启动 Whistle 后输出的信息中会包含该内容,也可自己通过 ipconfig 命令查看
    • 服务器端口 就是 Whistle 代理设置的端口,默认为 8899,如果你自定义端口保持一致即可

    1685541099789.png

    1685541176663.png

电脑端关闭防火墙

通过上述操作后,针对大部分人来讲可能还是无法实现代理,因为 手机端的代理请求电脑端的防火墙 拦截了,此时就需要将防火墙暂时关闭即可。

防火墙不允许关闭怎么办?

有些小伙伴的办公电脑可能为了安全不允许你 直接关闭防火墙,因此你要查看防火墙对应的 白名单端口 有哪些,接着将 原本的 Whistle 代理端口 切换到 符合的端口 即可,没有的话可以自己添加 白名单端口

值得注意的是,你切换了新的 Whistle 代理端口 后,你的 CA 证书 需要重新安装,最好是电脑端和手机端都重新安装,因为我们要保证手机端在通过代理请求访问代理服务时是可信任的。

Http Status = 0

定位提示原因

确定提示来源

首先会提示这个信息,那么得先确定这个提示的来源,到底是后端返回的,还是前端自定义的,于是把 提示文本 在项目中一搜索就匹配到了,这显然是前端自定义的提示:

image.png

分析提示原因

从代码逻辑上看很明显,就是当请求遇到错误时,就会触发 error 事件,于是就会向外返回提示文本并进行提示,并且还会输出相应的相关信息。

配合日志定位

但值得注意的是:提示内容包含了 xhr.status,再看看实际提示内容:

image.png

再看看 监控日志,如下:

image.png

哦豁!哦豁!哦豁!

xhr.status = 0 !!!

057443E8.gif

说实话我还是 第一次 遇到这种 状态码,这不免引起我的兴趣,这不得继续了解了解!

Whistle 抓包 + Chrome DevTools 远程调试信息汇总

Whistle 抓包异常信息

image.png

Chrome DevTools 远程调试信息

image.png

结合以上抓取到的信息,我们大致得到相关的信息就是:

  • ERR\_CONTENT\_FAILED

    • 这个错误也许你没有见过,但它在这可 不是一个无关联的错误,由于响应头的中 content-type 值为 application/json; charset=utf-8,而异常接口显然返回了 不符合编码格式的内容,因此发生了 ERR_CONTENT_FAILED 错误,即 属于同一个问题的不同表现 罢了
  • XMLHttpRequest 请求异常触发 xhr.onerror,但具体是什么异常未知,导致 xhr.status = 0

什么场景下 Http Status = 0 ?

XMLHttpRequest.status

XMLHttpRequest.status 是 XMLHttpRequest 响应 中的一个 无符号短整型 的数字状态码,即其值就是对应的是标准的 HTTP status codes,而 HTTP 响应状态码 又被归为以下五大类:

看着没有问题啊,但是你有没有发现 状态码 0 不在这常见的五大类中!!!

XMLHttpRequest.status 为啥是 0?

实际上在两种情况下 XMLHttpRequest.status = 0

  • 请求完成前status 的值为 0
  • 如果 XMLHttpRequest 出错,浏览器返回的 status 也为 0

请求未完成

请求未完成时 XMLHttpRequest.status = 0 的情况直接看代码示例就知道了,如下:

var xhr = new XMLHttpRequest();
console.log('UNSENT', xhr.status); // UNSENT(未发送)0

xhr.open('GET', '/server', true);
console.log('OPENED', xhr.status); // OPENED(已打开)0

xhr.onprogress = function () {
  console.log('LOADING', xhr.status); // LOADING(载入中)200
};

xhr.onload = function () {
  console.log('DONE', xhr.status); // DONE(完成)200
};

xhr.send(null);

但这显然不符合现在的场景,因为它是在 xhr.onerror 已经被触发。

XMLHttpRequest 出错

这里又会分为几种情况,其实在,具体如下:

  • 请求中止,例如基于 xhrInstance.abort() 将请求中止

    image.png

  • 发生跨域

    image.png

  • 错误的 URL 或 协议头

    image.png

  • 服务端提前结束响应,即未正常进行响应
  • 网络断开,网络异常导致操作过程请求无法正常发送等
  • ...

前端问题?后端问题?

相信不少前端同学看到诸如下图所示的 AJAX\_ERROR 时,就直接撒手不管了,交给对应的后端去看,正常来说确实没问题,但是总归要排查原因(不然凭啥说不是你的问题),例如这里存留的疑问点是:

  • PCMobile 使用的同一个接口,PC 端各个 请求正常,只有 Mobile 端有 异常请求
  • Mobile 端项目在 本地启动(即开发环境和已部署的测试服务),然后以浏览器的方式访问项目,并按 复现步骤 进行操作时是 正常请求,并不会有 异常请求 的情况出现

image.png

排除前端嫌疑

为了证明不是前端的问题,我们就需要 列举出可能由前端引发问题的情况,而归结起来就是一种情况,那就是 " 请求被终止发送 "

  • 手机端的 webview 存在 缓存 Ajax 请求 的机制,当 webview 检测到某个 Ajax 请求缓存可用时,会将新的请求 取消 掉,此时 xhr.status = 0

    • 这个是在查询资料过程中看到的一种可能,也好验证,那就是 清除缓存即可,但实际尝试后发现无效,证明不是此情况
  • 手机端 网络异常,请求未能正确发送

    • 这种情况也是不对的,因为除了用户在生产上出现异常请求外,我们在 测试环境 也能够 复现

除此之外,仔细看 Whistle 抓包 结果,异常请求的 result = 200,证明前端请求已经正常发出,只是 后续响应过程出现问题

0101C36D.jpg

排除后端嫌疑

后端可能引发问题的情况,总结起来也是一种情况,那就是 " 提前结束响应 "

  • 对接口请求频率有限制,例如频繁请求同一个接口时,多余请求可能就会被忽略

    • 而后端根本没有对请求做任何限制,并且再进行响应时,都会有 输出日志记录,在查找对应的日志后发现,对应的 异常请求并不在日志记录中,因此不是这种情况
  • 响应过程出现编码错误,因为在抓包过程中我们看到了,响应体 body 中是乱码的,不符合响应头中 content-type: application/json; charset=utf-8 的编码格式

    • 基于测试环境的后端日志输出,发现不存在异常请求的情况,因为即便是编码错误,也会有日志记录,因此也不是这种情况

以上情况说明,当产生异常请求时,根本没有进入到后端的 Control 层,因此后端嫌疑也排除了。

00F4B33F.jpg

交由中间链路进行排查

既然排除了 前端/后端 的嫌疑,那么剩下的可能就是 中间链路 的问题了,用户在手机端访问目标站点时的 链路,如下:

  • 手机 -> 网关 -> Nginx -> 应用服务 -> Nginx -> 网关 -> 手机

链路.png

而目前看 应用服务 返回的是没有问题的,因此剩下的就交由 网关运维 去进行排查了,跨部门协作 会有一些时间上不对等的问题(懂的都懂),后续具体原因会同步到此处。

034430E1.jpg

后续】排除运维嫌疑

下面是一段对话,这里只表达大致的意思:

  • 前端 A@ 运维 C,上面的问题后来服务端加过日志看过,前端收到乱码,服务端返回的是正常的,还有服务本地部署,通过 whistle 将测试环境的接口转发到本地服务,也不会出现乱码的情况
  • 运维 C@ 前端 B,一起帮忙看一看
  • 前端 B】上次和 前端 A 一起看过了, 查到乱码的数据是服务端返回的, 其他的就没法查下去了,看样子移动端的接口, 应该经过了 好几层的代理,所以现在想看下响应从服务端出来后,经过 ngnix 的时候是否正常
  • 前端 A】是的,现在的想法就是跟随 响应路径 的返回一层一层去排查
  • 运维 C】移动端测试环境是 iis ,不是 ng 代理

2E2730B8.png

总结一下,意思就是只有 生产环境ng 代理测试环境 是没有 ng 代理 的,那现在的情况是:生产环境可复现,测试环境可复现,因此不应该是 ng 代理 的问题,那么只有一种可能:网关

2F3F6DDE.gif

静等 网关 排查中 ......

f95400993e6b1d9ccbb554e02d8cf97.png

最后

欢迎关注同名公众号《熊的猫》,文章会同步更新,也可快速加入前端交流群!

以上就是本文的内容了,大致经过:出现问题 —> 复现问题 —> 排查/定位问题 —> 验证、排除嫌疑 —> 跨部门协作 等过程。

也许你觉得一开始就直接交给 后端、网关、运维 去排查就好了,毕竟一通操作下来发现不是自己的问题,还浪费了时间等等。

但是要明白的是当我们需要把某个问题交给 其他部门 进行排查/定位时,并不是我们随便说一句就可以了,因为大家都有很多事情要做,对你紧急的事情在其他部门中的优先级并不一定是最高的,此时还需要 部门和部门的负责人沟通协调,而在这之前最有必要的是先确保我们这一方是真的不存在问题的,然后还得把你的信息同步给其他部门同事,方便他们更快速的了解情况,更好、更快的实现互相协作和解决问题。

010FB4C3.png


熊的猫
966 声望339 粉丝

业精于勤立不易方,而后鹏程万里!