1 问题描述

在正式环境中,上传文件 [2003年鉴].docx 时,报错400。

但是,只有此文件上传时会报错,其他文件是正常的。

requests get 返回400 get请求返回400_tomcat

(后文为了方便描述,将问题文件称为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()转义