Java web 乱码问题从我学习Java的时候就已经开始出现,这次业务从ECS迁移到Docker又出现了,深入的了解了一下。
Tomcat 版本: 7.0.99 版本 ,最后说说Tomcat 8.5.50版本
乱码的原因和解决方法
乱码产生的原因是因为字节流编码和解码格式的不一致产生的,而在Java Web中产生编码和解码有几处,分别是
- http请求的URI ==> /view/陈顺/1.json
- http请求的参数queryString. /view/陈顺/1.json?name=陈顺
- header和cookie等等
- 2021-01-15 补充
- Tomcat 8.0.32分别使用自定义的WebUtil和HttpClient查询,前者不乱码,后者乱码
- 出现这个问题的原因是
application/x-www-form-urlencoded;charset=UTF-8
,分号后边的charset=UTF-8
未添加,这个版本的Tomcat 认为没有指定charset
就会使用ISO-8859-1
进行编码 - 这里是我不小心未添加
- 出现这个问题的原因是
- Tomcat 8.0.32分别使用自定义的WebUtil和HttpClient查询,前者不乱码,后者乱码
而在业务中常见的是第一类和第二类。
以下仅针对Tomcat 7.0.99 ,但是对Tomcat任意版本都适用
- 第一类的出现是URI==>/view/{page}/1 ,它产生乱码的原因是解码使用的URIEncoding和编码使用的不一致,在7.0.99版本当中,默认未设置 URIEncoding=UTF-8 导致其使用Servlet规范的解码标准 ISO-8859-1 , 因此解决这类乱码的原因是在 Connector上增加 URIEncoding=UTF-8
- 第二类乱码的原因同上,使用了默认的ISO-8859-1. 解决办法是增加 usebodyencodingforuri= true 参数 并且增加Spring的便解码Filter,该参数的含义是使用 RequestBody的编解码格式来解码queryString. 而RequestBody的解码格式是可以使用setCharacterEncoding 来进行修改的
题外话,网上搜索 setCharacterEncoding 对应GET请求无效,会发现很多网友都做了不恰当甚至是错误的解释,没有找到问题的关键就在下药了。自然不能解决这类乱码问题
对GET请求和POST请求的不同处理,在Tomcat内部是这么处理的
GET请求,当且仅当 queryString有数据时进行解析,解析方式如下
- 获取characterEncoding,如果不为空并且usebodyencodingforuri为true,那么设置queryStringEncoding=characterEncoding.
//org.apache.catalina.connector.Request#parseParameters
String enc = getCharacterEncoding();
boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();
if (enc != null) {
parameters.setEncoding(enc);
if (useBodyEncodingForURI) {
parameters.setQueryStringEncoding(enc);
}
} else {
parameters.setEncoding("ISO-8859-1");
if (useBodyEncodingForURI) {
parameters.setQueryStringEncoding("ISO-8859-1");
}
}
parameters.handleQueryParameters();
- 获取characterEncoding,如果不为空并且usebodyencodingforuri为true,那么设置queryStringEncoding=characterEncoding.
POST请求,Tomcat会使用其携带的Context-type请求头来进行解析,一般团队都会使用Filter来进行设置RequestBody的解码格式,因此POST一般不会出现乱码
- 小心charset丢失的情况.
小结
使用URIEncoding解决URI的乱码问题,使用usebodyencodingforuri+Filter解决GET请求参数乱码的问题.
Tomcat 8.5.50
当升级到Tomcat 8. Tomcat使用UTF-8做为默认的解码格式,因此不需要设置即可解决乱码的问题。
The default value of URIEncoding attribute for HTTP and AJP connectors has been changed from "ISO-8859-1" to be "UTF-8" (if "strict servlet compliance" mode is off, which is the default). This setting specifies what character encoding is used to decode '%xx'-encoded bytes in path and query of a request URI.
If server is configured with "strict servlet compliance" on, the default value of URIEncoding attribute of connectors is "ISO-8859-1", the same as in older versions of Tomcat.