近期项目中遇到一个bug,其中解决过程比较有意思,特此记录下来。有一天看到报警记录有一个500服务端的错误,量很少,一周都不一定有一个,先根据服务器里的本地日志拿到了当时请求的相关信息像UA、cookie什么的,确定了请求是来自iPhone上的qq浏览器,因为我们业务在手机浏览器里没有入口,所以这种情况很少。于是找来iPhone下载QQ浏览器尝试复现问题。
bug情景复现
描述:iPhone中某些版本的QQ浏览器中提交订单时,报错提示服务端异常,经抓包排查发现提交订单时post请求的body为空,content-length为0,于是开始了这次艰难蛋疼的bugfix过程。
其中手机是iPhone 8p,QQ浏览器版本9.1.0.4110,ios版本11.0,请求UA是Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.3.5 (KHTML, like Gecko) Version/11.0 MQQBrowser/9.1.0 Mobile/15B87 Safari/604.1 MttCustomUA/2 QBWebViewType/1 WKType/1。(虽然这些对这次后期排查修复都没什么蛋用,但有时还是很有用的)。
思路
个人喜欢debug时用排除法,先确定问题所在
首先排查发post请求过程有没有问题,因为有可能依赖的某个包在国产QQ浏览器上有兼容性问题,但一路排查发起过程都没什么问题,最后不用依赖包,直接用XMLHttpRequest手写了个post请求依然不好使,排除了发请求的依赖包的问题。排查项目是否对发请求有影响,有些内部的监控上报的包可能会对XMLHttpRequest中方法做改写,于是一路删除怀疑的依赖包,最后单纯返回了一个简单页面,页面内发一个post请求,body依然为空,至此排除了所有依赖包的影响(这个过程很费时间,这个思路不是很好)。排查到这就有点蛋疼了,Google搜了下看有没有人和我遇到同样的问题,发现qq浏览器论坛上17年就有人提出这个问题了,相似的帖子大概有七八个,但有官方回应的也就一两个,回复也只是说让升级浏览器。。。用户升级浏览器还行,开发者得解决问题啊,排查到这已经确定了一点,就是qq浏览器肯定对post请求的支持是有问题的。 意识到qq浏览器有问题,下意识想针对qq浏览器做下兼容处理。开始想post改成get请求,但是post中的body不是简单地数据结构,如果是以下数据改成get还好,如:
body { name: 'frontend',age: '8', }
但是遇到较为复杂的数据结构就不好使了,如:
{times: [123,234,2423,12],favour: {detail: 'sdadfa',} }
因为改成get后是通过query来传递数据的,query是只支持字符串的;另外这种方式隐隐约约感觉会坑很大,所以排除了这种方式。于是尝试另一种方式,把body数据放到header里面,反正HTTP协议里对header大小是没做限制的,所以这样干的
const xhr = new XMLHttpRequest(); xhr.setRequestHeader({x-body: JSON.stringify(body), })
服务端接收时再判断header中是否有x-body,当时想的这种方式是可行的,但现实很残酷,这种方式测试时发现抓包都抓不到,说明请求都没发出来,把body数据改小点,测试是可以的,这说明qq浏览器对header里数据大小是有限制的,(这就是理论与现实的矛盾的问题,http协议其实对这些都是没有限制的,但各个浏览器会有各自不同的限制),这种方式也宣布流产,此时已经中度蛋疼了。
后来想POST请求body为空这种问题还是性质挺严重的,其他页面是不是有这种问题呢?在别人页面打开找post请求发现人家的请求是有body的,这就说明我们项目中是肯定有问题的,但是同时有其他人反馈有过这种问题,说明qq浏览器也是有问题的。
既然这边不好debug到具体原因,那就向qq浏览器反馈下吧。先是向qq浏览器论坛提交了反馈,但是看之前帖子无人回应,估计这次反馈也会迟迟得不到回应;又找了QQ浏览器的反馈群,在群里反馈这个问题,这个回应还是比较快的,一天以后就有人回应了,在提供了问题页面,复现版本后,qq浏览器那边首先回应是让升级浏览器看好不好使,我是开发者啊,保留现场还来不及呢怎么再去破坏现场,把这种敷衍怼回去后,他们认真帮复现了下,最后确认了问题是旧版本的一个bug,最新的9.1.1上已经解决了,事情到这总算有个眉目了,但是还没算解决啊,他们回应说要么提醒用户升级到最新浏览器,要么页面请求时,服务器响应一个content-type,我擦,我们的问题终于出现了,这说明我们页面请求时没有返回content-type啊,他们解释说页面返回时没有带content-type,导致识别有问题。我检查了下果然没有返回content-type,之前一直纠结在接口请求上了,却忽视了页面请求时的header内容。问题找到了,也便容易解决了,在项目服务端渲染返回页面时加上了content-type,顺手向项目用的脚手架提了issue和pr。至此问题终于算是解决了总结
问题就是qq浏览器中post请求时body为空,需要在页面返回时有content-type,否则在一些版本的qq浏览器中会识别有问题,导致body为空。
在这个过程中,我意识到两个问题
遇到问题,特别是比较难解决的问题,一定不要轻易放弃,深挖下去总能有些意想不到的东西,这就是经验,而且解决了也会有成就感。遇到不是很有思路的问题,不要慌,不要轻易用打补丁做兼容的方式来解决问题,比如post改get,且不说语义不对,代码不优雅,后期也有可能带来其他问题。还是要找问题的根源从而解决问题。 |
|