J2EE 自学系列 —— Servlet 与浏览器 Request 和 Response 乱码解决方案

2 篇文章 0 订阅

一、问题背景

我在浏览器端使用 GBK 编码,以 GET 方式向 Servlet 发送了一个 request(包含中文),结果无论我怎么解码,都无法还原回原来的中文。

二、解决方案

  • 我们需要去%CATALINA_HOME%/conf/server.xml,增加一行配置:useBodyEncodingForURI="true"
  • 在 Servlet 获取属性前,先调用req.setCharacterEncoding("GBK");,如上问题,则使用GBK编码。
<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
			   useBodyEncodingForURI="true"
               redirectPort="8443" />

三、方案解析

  • 以GET 方式,Tomcat 会对属性先进行一次解码,解码方式为 UTF-8,而我们提交的属性,是以 GBK 进行编码,二者编码不一致,故乱码;
  • 只设置 req.setCharacterEncoding("GBK");,对该问题是没作用的,因为该接口只会影响 Body 部分的编码,而以 GET 方式提交,属性是携带在请求头。Tomcat 对 header 跟 body 的编解码方式是不一样的,后文会提到。
  • 在设置 req.setCharacterEncoding("GBK");的基础上,我们可以再设置useBodyEncodingForURI="true",强制让 header 以解码 body 的编码方式进行解码,那么req.setCharacterEncoding("GBK");这个设置就能生效了。

四、其他编解码的知识点

在解决这个问题的时候,我对编解码有了一个较为全面的认知,此处再简略提一些有关于编解码的知识点,以供读者解决相关问题时提供一些思路。

  • Tomcat header 跟 body 的编解码方式不一样。 以下代码是我去Tomcat 源码上摘抄的,说明 Tomcat 原本就有这样的设计:如果不指定编码格式,那么对于 header,将默认采用 UTF-8 进行编解码,对于 body 将默认采用 ISO_8859_1进行编解码
public static final Charset DEFAULT_URI_CHARSET = StandardCharsets.UTF_8;
public static final Charset DEFAULT_BODY_CHARSET = StandardCharsets.ISO_8859_1;
  • Tomcat 对 header 默认采用 UTF-8 进行编解码,是从 Tomcat 8 开始的,之前的版本 还是默认采用 ISO_8859_1 进行编解码(由于笔者是用 Tomcat 9,此点未进行验证
  • 浏览器显示时的编码,跟浏览器接收到 Response 的进行解析的编码,可能是不一样的(这个可以阅读下文章末尾的引用
  • req.setCharacterEncoding()resp.setCharacterEncoding()均只对 body 部分的编解码有作用,指定了resp.setCharacterEncoding(),那么在浏览器解码该响应时,必定不会乱码,但浏览器正文会不会乱码,就不一定了。
    在这里插入图片描述
  • 如果想确保浏览器不乱码,那么还需要设置resp.setContentType("text/html;charset=UTF-8");,告诉浏览器以什么方式解码。
  • 如下图,针对解码后再编码,发现跟原始数据不一致的情况,是因为乱码后不可逆了,对于非乱码的字符,还是可逆的。如图代码URLDecoder.decode("%C4%D0", "UTF-8");%C4%D0URLEncoder.encode("男", "GBK");的结果)解码的字符为“�”(编码为0xFFFD),这个字符是 UTF-8 转码失败后的默认字符,因此是多对一的关系,不可能逆向编码回原来的编码。所以遇到这种情况,除非能拿到原始的编码数据(或者对 Tomcat 的解码过程找到相对应的钩子方法进行干涉,例如本文的解决方案),否则这种乱码问题就解决不了了。
// java.nio.charse.CharsetDecoder.java
protected CharsetDecoder(Charset cs,
                             float averageCharsPerByte,
                             float maxCharsPerByte)
    {
        this(cs,
             averageCharsPerByte, maxCharsPerByte,
             "\uFFFD");
    }

在这里插入图片描述

五、参考文章

一文让你从此告别HTTP乱码(一)
一文让你从此告别HTTP乱码(二)
深入分析 Java 中的中文编码问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值