Servlet请求转发至html页面中文乱码问题与分析

起因

在编写servlet代码的时候,练习重定向和请求转发,发现重定向至html页面时,页面显示中文正常,使用请求转发至html页面时,显示中文乱码,两个servlet都在doGet()方法内首先使用了resp.setCharacterEncoding("UTF-8");设置编码为UTF-8

核心代码

各部分核心代码如下:

重定向repRedirect代码:

@WebServlet("/repTest")
public class repRedirect extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        String username=req.getParameter("username");
        String password=req.getParameter("password");

        if(username.equals("admin")&&password.equals("123456")){
            resp.sendRedirect("/Unit7/welcome.html");
        }else{
            resp.sendRedirect("/Unit7/fail.html");
        }
    }
}

请求转发repForward.java代码:

@WebServlet("/repForward")
public class repForward extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        resp.setContentType("text/html;charset=utf-8");
        resp.setCharacterEncoding("UTF-8");
        resp.setHeader("Content-Type","text/html;charset=UTF-8");
        String username=req.getParameter("username");
        String password=req.getParameter("password");
//        if(username.isEmpty()&&password.isEmpty()){
//            resp.sendRedirect("/Unit/welcome.html");
//        }
        if(username.equals("admin")&&password.equals("123456")){
            req.getRequestDispatcher("/Unit7/welcome.html").forward(req,resp);
        }else{
            req.getRequestDispatcher("/Unit7/fail.html").forward(req,resp);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

跳转的html页面代码

welcome.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h3>欢迎登陆</h3>
</body>
</html>
fail.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h3>登陆失败</h3>
</body>
</html>

访问效果

访问重定向页面

http://127.0.0.1:8080/repTest?username=admin&password=123456

img

访问请求转发页面

http://127.0.0.1:8080/repForward?username=admin&password=123456

img

可以看出请求转发出现了乱码

我们再故意输错密码,测试一下请求转发失败的页面fail.html

http://127.0.0.1:8080/repForward?username=admin&password=1234

img

同样乱码

解决方法(1)

网上已经有人给出了解决方法,即将请求转发跳转的html编码设置为GBK,如下

img

重新访问请求转发页面

http://127.0.0.1:8080/repForward?username=admin&password=123456

img

此时乱码恢复正常

fail.html页面并没有修改为GBK,我们访问它试试,进行对照比较

http://127.0.0.1:8080/repForward?username=admin&password=1234

img

依旧乱码,可以看出是设置GBK生效

原因分析

虽然这样设置能够让页面的乱码正常,但是我们实际上是在servlet里面设置了UTF-8编码,为什么页面上还需要改为GBK,而为什么请求转发和重定向都设置了UTF-8但只有请求转发的页面显示乱码了

在这篇博文中提到

因此,我认为一个HTML文件在集成开发创建时是UTF-8的格式,这个格式在IDE上应该可以设置,但在打开时是更具默认编码格式打开的(即ANSI),因此会产生乱码

而在我们的讨论+猜测下,请求转发的过程类似于

img

图中的GBK指的是servlet默认读取文件的编码方式,不同地区的电脑可能默认不一样

在另一篇博文对请求转发详细流程的介绍中也提到:

forward() 方法的处理流程:
  ● 清空用于存放响应正文(响应体)数据的缓冲区。
  ● 如果目标组件为Servlet 或JSP,就调用它们的service() 方法,把该方法产生的响应结果发送到客户端,如果目标组件为文件系统中的静态 html 文档,就读去文档中的数据并把它发送到客户端。

也就是说,请求转发是有一个读文档数据的过程,侧面论证了图示

读文档数据涉及到了文档原来的编码和读取文件的编码方式,分析可得,出现乱码的原因是,文档本身的编码是UTF-8,但servlet读取时是使用默认编码方式GBK读取的。

而我们在servlet中设置编码的过程,也就是图中的第二部分,所以不管我们用什么样的方式设置UTF-8,都只会让只能使用GBK解析的页面乱码

当然这一部分还是猜测,所以我们用另外两种方法来验证我们的观点

论证1

不管编码解码的结果如何,我们想要传递的数据字节流是不变的,它不会因为乱码字符就从登陆成功变成登陆失败,换一句话说,不管用中文还是英文表达同一件事情,其内核都是表达的这件事,并不会因为使用的语言而改变

既然文档本身的编码是UTF-8,servlet读取方式为GBK,我们把文档本身编码修改为GBK,如果上面的推断是正确的,这样做应该就能够让乱码正常

流程为:

GBK -> GBK -> 字符流 -> UTF-8 ->UTF-8页面显示

打开VScode,方便转换编码

img

可以看到文件此时保存的编码为UTF-8

我们修改其为GBK,点击通过编码保存

img

设置成GBK

img

可以见到此时文件保存编码修改为了 GBK

img

这个时候再启动servlet请求转发

访问 http://127.0.0.1:8080/repForward?username=admin&password=123456

显示正常

img

侧面反映了我们思考的过程是正确的

论证2

配置XML文件

<jsp-config>
    <jsp-property-group>
        <url-pattern>*.html</url-pattern>
        <page-encoding>UTF-8</page-encoding>
    </jsp-property-group>
</jsp-config>

意思是,任何以html结尾的URL请求的资源,都以UTF-8格式打开,这里也就是修改servlet的读取文件编码方式,此时将welcome.html文件的编码修改回UTF-8

img

img

重启服务,访问请求转发页面

http://127.0.0.1:8080/repForward?username=admin&password=123456

此时访问的页面编码依旧没有乱码,说明jsp-config起了作用,同时确实是因为servlrt默认读取html文件的编码方式为GBK

img

为了验证真的是jsp-config的作用,将其删除再试一次

删除后访问乱码

img

证明确实servlet默认读取html文件编码方式为GBK,这个默认方式不同的地区应该是不同的,例如美国应该是ASCII

总结

两个论证中的方法也都能解决请求转发至html页面乱码的问题

乱码出现的原因是文件本身编码和servlet默认读取文件编码方式不一样而导致的

至于为什么重定向的页面显示中文是正常的,我们认为是请求转发和重定向这两个功能的实现在servlet中不一样,比如请求转发是需要读取转发的html文件,重定向是让用户自己重新去访问html页面,缺少了默认读取的部分,具体的实现区别需要看servlet的源代码才能知道

参考链接

END

建了一个微信的安全交流群,欢迎添加我微信备注进群,一起来聊天吹水哇,以及一个会发布安全相关内容的公众号,欢迎关注 😃

GIF GIF
posted @ 2021-09-23 00:29  春告鳥  阅读(2918)  评论(3编辑  收藏  举报