目录
2. 黑名单法(和第1个方法类似,但此法有缺陷,写在这个方法的末尾了)
前言
说是解决,不如说是补丁。(正常情况下估计遇不到这个问题)
如果你遇到了这个问题,那说明你用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
加几行打印看看是个什么妖魔鬼怪编码乱我道心
@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
可以发现,原本的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
如果你只是单纯用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
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:有一说一,自我感觉写的挺明白的,真解决方法也写的挺清楚