首先说明一点,以下的测试方法只有一个HttpServletRequest.forward,但是基于原理上的讲解,其他乱码问题应该也可以从中得到一些启示。不敢保证百分百正确,但能提供一个大致的方向。
下面为测试入口servlet的代码,其中的getWriter被注释掉,后面讲其作用。
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
//PrintWriter out=resp.getWriter();
resp.setHeader("Expires","0");
resp.setHeader("Cache-Control","no-cache,no-store");
resp.setHeader("Pragma","no-cache");
req.getRequestDispatcher("/WEB-INF/jsp/in2.jsp").forward(req,resp);
}
测试流程就是很简单的从一个servlet转到另一个jsp或者html上。
首先我们应该知道,jsp页面实际上会转换成一个servlet,至于存在那个地方,可以自行百度,不同的系统以及集成开发环境会使之存在不同的地方。下面为测试的第一个jsp文件in2.jsp,很简单,直接输出一段话。
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8"%>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
out.print("我是in2.jsp文件的内容");
//out.print("参数为:"+request.getParameter("param"));
%>
</body>
</html>
其中的page里面设置的几个UTF-8编码有必要讲下它的作用范围:
下面的jsp转换成一个servlet的流程必须清楚:
上述in2.jsp文件是UTF-8格式保存的,接下来打开它转换成的servlet,片段代码如下,可以看到没有乱码
out.write("\r\n");
out.write("\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write(" <title>Title</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.print("我是in2.jsp文件的内容");
//out.print("参数为:"+request.getParameter("param"));
out.write("\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
浏览器端的输出也是正常的
现在我们改动pageEncoding=ISO-8859-1
再次查看jsp转换成的servlet
out.write("\r\n");
out.write("\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write(" <title>Title</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.print("ææ¯in2.jspæ件çå容");
//out.print("åæ°ä¸º:"+request.getParameter("param"));
out.write("\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
可以看到已经乱码了,这就是以ISO-8859-1编码打开UTF-8编码文件时产生的乱码,网页输出端也是如此
因此,可以总结出,如果要保证不乱码,必须以文件编码的格式打开文件,而且还得指定浏览器以同样的编码格式解码。这儿并未测试response设置编码格式,但得注意这点!
test.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>测试jsp</title>
</head>
<body>
<jsp:forward page="in2.jsp">
<jsp:param name="param" value="data数据"/>
</jsp:forward>
</body>
</html>
in2.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="ISO-8859-1"%>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
out.print("我是in2.jsp文件的内容");
out.print("参数为:"+request.getParameter("param"));
%>
</body>
</html>
入口servlet
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// resp.setContentType("text/html;charset=utf-8");
// PrintWriter out=resp.getWriter();
resp.setHeader("Expires","0");
resp.setHeader("Cache-Control","no-cache,no-store");
resp.setHeader("Pragma","no-cache");
req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,resp);
}
网页端得输出为以下情况:
in2.jsp文件的内容没问题,test.jsp中设置的参数汉字乱码,这个也可以从test_jsp.java里面看到原因,是因为这个"data数据"是更据request的编码格式来写入的,默认为ISO-8859-1,将其改变为utf-8则正常显示。在设置参数之前改变编码,不要在乱码之后改变编码,那时没用了,可以在jsp里面改变,也可以在入口servlet改变,如req.setCharacterEncoding("utf-8");。
静态HTML文件如下in1.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html" charset="UTF-8">
<title>Title</title>
</head>
<body>
这是in1.html里面的内容
</body>
</html>
入口servlet的代码如下:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
PrintWriter out=resp.getWriter();
resp.setHeader("Expires","0");
resp.setHeader("Cache-Control","no-cache,no-store");
resp.setHeader("Pragma","no-cache");
req.setCharacterEncoding("utf-8");
req.getRequestDispatcher("/WEB-INF/jsp/in1.html").forward(req,resp);
}
结果为如下,乱码了,归根揭底其实也是pageEncoding的原因
因为HTML文件无法设置pageEncoding,也不会生成servlet,所以我也不能确定是不是pageEncoding的原因,因此作出假设,转发到HTML后,HTML的打开方式为以下2种情况。
以notepad++打开这个in1.html,并且将编码格式设置为ANSI,则结果如下,刚好和浏览器输出一致
因此我猜测是以ANSI的格式打开HTML,这个格式会因操作系统和地区而已,中国WINDOWS为GBK格式
下面我另存为一个in2.html格式保存为ANSI
并将servlet的转发到in2.html,输出正常
因此,我认为一个HTML文件在集成开发创建时是UTF-8的格式,这个格式在IDE上应该可以设置,但在打开时是更具默认编码格式打开的(即ANSI),因此会产生乱码,当然,这只是找到了原因。而解决的办法就是将这个HTML以我们想要的格式打开,即UTF-8,但又不能设置pageEncoding,因此我们可以将HTML当作JSP处理,静态HTML是可以转成JSP的。
这种途径可以配置XML文件,添加如下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格式打开,因此在次转到in1.html则正常了。如果配置了XML就不能在访问in2.html,会乱码,因为in2.html的编码格式为ANSI,以UTF-8的格式打开会乱码。
关于最开始说的PrintWriter out=resp.getWriter的作用关于另一个知识点,对于一切没有在XML中配置的servlet,都是采用缺省servlet访问,关于缺省servlet,可以百度查看更多。如上面测试时,如果在转发之前没有使用getWriter,则缺省servlet使用的是字节流输出,如果使用了getwriter则使用字符流输出,字节流同理。关于content-type头字段的charset编码格式和字节流字符流之间又会产生多种情况,如使用字符流,但不指定charset,则默认的格式为ISO-8859-1,用来输出中文则会乱码;使用字节流时,不论是否设置为utf-8,都不会产生乱码,按理字节流使用utf-8输出中文会乱码,我没有去看这个缺省servlet的源码,只能猜测使用字节流时都采用ISO-8859-1来输出。