1 问题描述
在正式环境中,上传文件 [2003年鉴].docx 时,报错400。
但是,只有此文件上传时会报错,其他文件是正常的。
(后文为了方便描述,将问题文件称为a)
2 分析
2.1 找到出错原因
400报错,一般是请求参数和服务器接收参数的格式不同导致的。
但只有文件a出错,其他文件都正常。可能是文件a有什么特殊之处,触发了隐蔽的bug。
最先猜测,可能是 a 的内容有问题,但是用 office 打开后看内容,很正常。
那么,可能是标题有问题。我把 a 改名为 1.docx ,然后重新上传。这样就不会报错了!
进一步排查发现:是标题中的特殊符号[ 和 ] 导致报错400。
2.2 在开发环境,无法复现bug
问题找到了,就要解决它了。但是,又遇到了麻烦:在开发环境中无法复现bug。
用相同的代码、使用同一个电脑的同一个浏览器进行访问,同样去上传文件a(不改名),不会出错。
因为代码是相同的。并且是用同一个电脑的同一个浏览器进行访问,说明不是客户端环境或者浏览器的问题。那么,只可能是服务器环境的的差异了。
2.3 找到服务器环境的差异
逐个对比开发和正式的服务器环境(jdk版本、nginx配置、防火墙设置等等),最终发现,是 tomcat 版本不同。
部署环境的tomcat版本比较高,有新特性:
严格按照 RFC 3986规范进行访问解析,而 RFC 3986规范定义了Url中只允许包含英文字母(a-zA-Z)、数字(0-9)、-_.~4个特殊字符以及所有保留字符
因为get请求的参数是拼接在url后面的,所以参数中只要包含 [ 或 ] 都会报错400。
参考:
- tomcat 请求出现RFC 7230 and RFC3986的错误,解决办法
3 总结一下
tomcat高版本中,不允许url中有特殊字符。 上传 [2003年鉴].docx 时,会触发一个get请求,请求参数中包含文件名称。所以这个请求的url地址中会包括特殊字符[ 和 ] ,被tomcat直接拦下,报错400。
解决方法:
- 仍继续用高版本的tomcat,但是修改它的配置,让它不再去拦截有特殊符号的url
- 用低版本的tomcat
- 拼在url后面的参数,先用encodeURI()转义