解决servlet全局过滤器设置响应utf-8导致html乱码问题

封面镇楼图

 

目录

前言

来看看问题是怎么发生的

解决方案(又开始缝缝补补了):

1. 过滤html法(推荐,原因写在本方法末尾了)

2. 黑名单法(和第1个方法类似,但此法有缺陷,写在这个方法的末尾了)

3. 只过滤servlet法

4. 改配置法(不推荐)

5. 。。。

真 · 解决方法


前言

说是解决,不如说是补丁。(正常情况下估计遇不到这个问题)

如果你遇到了这个问题,那说明你用servlet,配置了全局过滤器,并且在前后端不分离的情况下,不用thymeleaf或其他框架,就直接用了html

来看看问题是怎么发生的

注:为了防止缓存带来的影响,本次我所有测试的刷新都是 Ctrl + F5 (这点很重要!!!)

先来看看我的全局过滤器代码,可以看到一个标准的乱码过滤器

@WebFilter(filterName = "EncodingFilter", urlPatterns = "/*")
public class EncodingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8"); // 处理请求乱码
        response.setContentType("text/html;charset=utf-8"); // 处理响应乱码
        chain.doFilter(request, response); // 放行
    }
}

然后新建了个html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>我认为不会乱码</h1>
</body>
</html>

一访问,诶,果然乱码了,如图1

图1 本不应该乱码的页面

 

 加几行打印看看是个什么妖魔鬼怪编码乱我道心

@WebFilter(filterName = "EncodingFilter", urlPatterns = "/*")
public class EncodingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {

        System.out.println("响应前ContentType: " + response.getContentType());
        System.out.println("响应前CharacterEncoding: " + response.getCharacterEncoding());

        request.setCharacterEncoding("utf-8"); // 处理请求乱码
        response.setContentType("text/html;charset=utf-8"); // 处理响应乱码
        chain.doFilter(request, response); // 放行

        System.out.println("响应后ContentType: " + response.getContentType());
        System.out.println("响应后CharacterEncoding: " + response.getCharacterEncoding());

    }
}

打印结果如图2

图2 打印结果

可以发现,原本的html的响应默认的ContentType是 "text/html",CharacterEncoding是 "ISO-8859-1"

我们的过滤器把html的响应改成了"text/html;charset=utf-8"之后才出现了乱码

解决方案(又开始缝缝补补了)

1. 过滤html法(推荐,原因写在本方法末尾了)

既然问题在于我们把html的响应改成了"text/html;charset=utf-8",那再弄个过滤器专门过滤html,把响应改回来不就行了。

此方法需注意,该html过滤器的过滤顺序得在我们的全局乱码过滤器的后面过滤,配置文件中的过滤器可以通过上下文先后来规定顺序,但在注解中的顺序则不太一样(我测试的时候发现是按注解设置的 filterName 的首字母排的)

@WebFilter(filterName = "HtmlEncodingFilter", urlPatterns = "*.html")
public class HtmlEncodingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        response.setContentType("text/html");
        response.setCharacterEncoding(null);
        chain.doFilter(request, response);
    }
}

然后成功了,如图3、图4

图3 成功页面

 

图4 打印结果

如果你只是单纯用html结果发现乱码,但又不想换jsp或者前后端分离或者用thymeleaf,那可以用这个方法

2. 黑名单法(和第1个方法类似,但此法有缺陷,写在这个方法的末尾了)

这个好理解,就是将不想过滤的后缀直接写在一个list或者数组中,获取到请求的链接,匹配到黑名单内的后缀就不设置 ContentType

@WebFilter(filterName = "EncodingFilter", urlPatterns = "/*")
public class EncodingFilter implements Filter {
    // 设置的黑名单后缀
    private final String[] noCheckList = new String[]{
            ".html",
    };

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8"); // 处理请求乱码
        String url = ((HttpServletRequest) request).getRequestURL(); // 拿到请求的URL
        for (String s : noCheckList) {
            if (url.endsWith(s)) { // endsWith方法可以直接匹配后缀
                System.out.println("匹配到黑名单后缀,直接放行");
                chain.doFilter(request, response); // 放行
                return;
            }
        }
        response.setContentType("text/html;charset=utf-8"); // 处理响应乱码
        chain.doFilter(request, response); // 放行
    }
}

 缺陷:当你的页面名为index.html,而浏览器直接访问这个文件所在的目录时,那就会默认访问该目录下的index.html,但是后台 getRequestURL() 方法拿到的URL的后缀是这个目录名而不是index.html,此时由于黑名单写的是 " *.html ",所以不会直接放行从而导致页面还是乱码。

如图5、图6

图5 无后缀版

 

图6 有后缀版

 

3. 只过滤servlet法

一般前台请求的都是servlet,过滤器路径改成只匹配servlet即可,这样html就不会被这个全局乱码过滤器所波及无辜,但这需要统一servlet的路径

比如你的 HelloServlet 的路径就得是"/servlet/hello"

@WebServlet("/servlet/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("进入到HelloServlet");
    }
}

这样你的过滤器路径就可以光明正大的写成 "/servlet/*" 了

@WebFilter(filterName = "EncodingFilter", urlPatterns = "/servlet/*")
public class EncodingFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8"); // 处理请求乱码
        response.setContentType("text/html;charset=utf-8"); // 处理响应乱码
        chain.doFilter(request, response); // 放行
    }
}

4. 改配置法(不推荐)

此法就是在idea的Tomcat配置中的vmOption里加上"-Dfile.encoding=UTF-8"(别把双引号也加上去了)

设置之后虽然html正常了,但我控制台乱码了(由于我以前Tomcat为了解决控制台乱码,所以一部分编码我改成了GBK,此时encoding=UTF-8,于是控制台又乱码了),所以我代表个人不推荐此方法(说不定以后有人能证明这方法好,只是我没用对呢)

5. 。。。

此方法名为创新,少年少女们呐,那名为解决方法的宝藏就在原理的大海中,去寻找新的解决方法把。于是开启了大航海时代


咳咳咳,接下来是吐槽真正的解决方法

真 · 解决方法

正常情况下来说

如果是前后端分离,那不会出现这个问题,因为这个过滤器原本应给是过滤后端servlet响应的,前端在另一台服务器或者当前服务器的另一个WebApp上,不会受到这个过滤器的波及。

如果是前后端不分离,你用的是jsp,也不会出现这个问题,因为jsp本质上也是servlet。

如果前后端不分离而且用html,那你应该用thymeleaf,这个的乱码在你配置模板的时候就应该配为utf-8,从而不会发生这个问题。

说到底,这个问题的起因还是因为想用html又不想把前端分离出去,于是html被全局编码过滤器波及了。

说到这里文章就结束了,什么?你说不知道真 · 解决方法是什么?

答案藏在文字中,用心去感受,你就能得到自己的理解,这才是你的真 · 解决方法

作者内心OS:有一说一,自我感觉写的挺明白的,真解决方法也写的挺清楚

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值