400错误
400错误来了
周五开发同学拉我说有用户升级到新版本后无法使用产品功能了,远程用Charles查看大部分接口发起的请求都返回了400错误,结果如下:

排查走起来
当时心里很慌,因为最近搬迁机房对线上的改动特别多,先忧虑会不会是自己这边对接入层改动引起的。随着翻遍了最近一周的更改,没有发现在接入层加入过一些请求逻辑的处理。继续排查Nginx日志,结合几个接口的访日日志对比发现异常的情况如下图,后端upstream_status 返回状态码的是400,Nginx拿到后返回给用户的也就是400状态码,deviceName字段带过来的电脑机器名是未encode的中文字符串,what ❓❓

查看后端服务日志,有两个比较异常的点:
- server端处理时候deviceName参数内容为转换为0xe50xa40xa70xe40xbb0xbb
- The valid characters are defined in RFC 7230 and RFC 3986

在虚拟环境中把Windows虚拟机计算机名改成中文“天上没有乌云盖”,100%复现异常,到这里基本可以确定这个deviceName未encode导致的400异常。

0xe50xa40xa70xe40xbb0xbb
这个是utf-8编码后的结果,正常情况下urlencode的结果应该是%E5%A4%A7%E4%BB%BB,如下图:

The valid characters are defined in RFC 7230 and RFC 3986
参考:The valid characters are defined in RFC 7230 and RFC 3986
导致上述问题是因为tomcat自8.5.x系列的:8.5.12 之后版本、8.0.x系列的:8.0.42 之后版本、7.0.x系列的:7.0.76 之后版本对URL参数做了比较规范的限制,必须遵循RFC 7230 and RFC 3986规范,对于非保留字字符(json格式的请求参数)必须做转义操作,否则会抛出Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986错误信息。就是严格按照 RFC 3986规范进行访问解析,而 RFC 3986规范定义了Url中只允许包含英文字母(a-zA-Z)、数字(0-9)、-_.~4个特殊字符以及所有保留字符(RFC3986中指定了以下字符为保留字符:! * ’ ( ) ; : @ & = + $ , / ? # [ ])。
0xe50xa40xa70xe40xbb0xbb 是一个十六进制编码的字符串,它不符合 RFC 7230 或 RFC 3986 标准。这两个标准规定了 URI(Uniform Resource Identifier)的语法和解析规则。根据这些标准,URI 应该由特定的字符集组成,并且应该遵循一定的格式。这个字符串不符合这些要求,因此不符合相关标准。
修复异常
APP端同学快速修复了这个异常,通过正常的更新或者下载官网最新包就可以解决这个问题。除了发的参数数据未encode之外,在公司无法复现还有个搞笑的原因,公司发的电脑计算机名按资产号来的,也没有人去改成中文,但是用户那边的电脑自行设置一个中文名字,全用户的实际使用场景也无法全部覆盖。
写在最后
一些http或https请求的参数,什么情况下需要urlencode编码
APP端给server端传数据都要encode一下才能减少这种400的发生~