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 补充
    • image.png
    • Tomcat 8.0.32分别使用自定义的WebUtil和HttpClient查询,前者不乱码,后者乱码
      • 出现这个问题的原因是 application/x-www-form-urlencoded;charset=UTF-8 ,分号后边的 charset=UTF-8 未添加,这个版本的Tomcat 认为没有指定 charset 就会使用 ISO-8859-1 进行编码
      • 这里是我不小心未添加

而在业务中常见的是第一类和第二类。

以下仅针对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.
      1. //org.apache.catalina.connector.Request#parseParameters
      2. String enc = getCharacterEncoding();
      3. boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();
      4. if (enc != null) {
      5. parameters.setEncoding(enc);
      6. if (useBodyEncodingForURI) {
      7. parameters.setQueryStringEncoding(enc);
      8. }
      9. } else {
      10. parameters.setEncoding("ISO-8859-1");
      11. if (useBodyEncodingForURI) {
      12. parameters.setQueryStringEncoding("ISO-8859-1");
      13. }
      14. }
      15. parameters.handleQueryParameters();
  • POST请求,Tomcat会使用其携带的Context-type请求头来进行解析,一般团队都会使用Filter来进行设置RequestBody的解码格式,因此POST一般不会出现乱码

    • 小心charset丢失的情况.

小结

使用URIEncoding解决URI的乱码问题,使用usebodyencodingforuri+Filter解决GET请求参数乱码的问题.

Tomcat 8.5.50

当升级到Tomcat 8. Tomcat使用UTF-8做为默认的解码格式,因此不需要设置即可解决乱码的问题。

  1. 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.
  2. 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.