大家在使用springcloud自己的gateway作为网关服务时,可能会不小心遇到自定义的Filter处理请求Request报文时出现400的错误,而且这个错误还不是每次请求都必现,额什么意思?难不成你是说请求还时好时坏?bingo!就是这么邪乎,这个问题让我也头疼了好是一阵,but最后还是被我攻克啦(出现问题不解决?可不是我的风格!)!那么在这里就跟大家分享一下此问题的解决思路,如果有帮助到您,还请继续关注我的后续文章,谢谢。
- 问题描述: 客户端请求网关服务时,当前后两次请求时间间隔大概5分钟内时(超过5分钟经测试发现请求正常),第二次请求会出现400 BadRequest错误!android必现,iOS出现频率较低。
- 解决思路:
- 注释掉除用于处理解密的filter(此filter无法注释掉,因为客户端正常请求必须要经过此filter处理,否则将出现无法访问的网关内部异常错误)之外的所有自定义filter,目的是排除法查找是否由于某一个filter写法存在问题所导致。-结论:仍出现400,定位问题失败!
- 回顾问题描述,因为不是每次请求都会出现400,同时5分钟为一个关键时间周期,认为可能需要设置请求时间参数,但检查后确认在使用的框架中无此功能,故决定升级Springcloud和SpringBoot到最新版( Hoxton.RELEASE、SpringBoot2.2.4),目的是确认是否是因为框架自身版本低的bug所导致。-结论:仍出现400,定位问题失败!
- 以上思路1和2均验证失败后,最终尝试将问题定位到思路1中无法注释掉的用于处理解密的filter。采取排除法将此filter中的代码依次注释掉定位是由于哪个位置导致的400。-结论:发现问题所在位置,是由于接收到的请求没有Content-Type项,导致走到了默认的返回Mono.empty()。
- 解决方案:
采用设置双保险策略。 设置保险1:为了提高处理效率,当前filter解密报文之前就进行判断,针对Content-Type等于null的情况,直接return chain.filter(exchange),交由下一个filter处理,filter链条继续。 设置保险2:针对类似Content-Type等于null的特殊异常情况,在研究Mono类的用法时发现,存在Mono.empty()、Mono.just()、Mono.JustorEmpty()共计三种方法,故使用Mono.JustorEmpty(data),通过判断data是否为空,来执行empty或者执行正常的just(data不为空执行just),走完如场景重现(见下文)中的index=6之后余下所有的filter,直到执行完成,正常结束所有filter处理。
- 解决效果: 策略应用后经测试没再出现400的情况,但是出现问题的原理有待进一步分析。
- 问题原理: 客户端请求网关时,经过Filter处理时,由于接收到的请求headers中会出现Content-Type为null的情况(这个就需要客户端的兄弟们检查一下请求接口中是否有漏传的情况啦),从而导致原有的filter中判断条件不满足,因为之前没有遇到Content-Type等于空的情况,所以Content-Type为空的时候默认就给他返回了Mono.empty(),执行了Mono.empty()之后,就认为所有的filter都执行完成了,filter链条处理结束。但是实际上,Content-Type等于空的时候,只执行到了如场景重现(见下文)中索引index=6的filter,此时filter链条并没有执行完成,直接结束就出现了400。
- 场景重现: 当前filter中判断到Content-Type为null,执行chain.filter(exchange),debug断点跟踪,如下图所示,解密filter的index=5,经过执行后现在已切换到了下一个filter即index=6,紧接着执行chain.filter(exchange)后面的Mono.empty(),从而导致filter链条提前结束,出现400。
好了,说到这里就先暂时告一段落啦,希望我的答疑能够帮助到大家!武汉加油!加油中国 !