很奇怪测试了多个应用服务器,只有Tomcat出现了乱码问题。让我们来分析一下原因,测试环境是Tomcat5.5.27,字符集编码统一为UTF-8。
1.页面静态内容乱码(非动态生成内容乱码)
这一般是<%@ page pageEncoding="UTF-8" %>设置的问题,建议在每个页面上都加上pageEncoding设定,让应用服务器能正确把JSP文件按照设定的编码转换为Java文件,只要这个pageEncoding设置正确就可以避免静态内容的乱码。有人可能会说我没有设置也没有乱码,那是因为应用服务器还可以读取<%@ page contentType="text/html; charset=UTF-8" %>中的charset作为备选方案,虽然这是JSP规范中要求的,但是难保有的容器没有实现或实现有BUG,所以有时候在某个应用服务器下(如Tomcat)不设置pageEncoding也可以,但是同样的页面拿到别的应用服务器下就不能保证不出现乱码。
2.动态生成内容乱码
新下载的Tomcat没有经过任何特殊的设置,无论是GET和POST都出现乱码。首先设置HTTP Connector(server.xml中监听8080端口的那个Connector),加上URIEncoding="UTF-8",消除了GET乱码,再在JSP页面中第一句加入<% request.setCharacterEncoding("UTF-8"); %>,消除了POST乱码。
通过上面两个设置我们发现,URIEncoding控制的是GET字符集编码,Request的CharacterEncoding控制的是POST字符集编码。
如果没有上面那句<% request.setCharacterEncoding("UTF-8"); %>,在页面起始加入<%= request.getCharacterEncoding() %>,在Tomcat下我们发现输出null,在其他服务器下却输出UTF-8。这就是为什么在Tomcat下应该正确设置Request的CharacterEncoding的原因。
上面提到的<%@ page contentType="text/html; charset=UTF-8" %>,除了声明返回给客户端的流是text/html外,同时设置了Response的CharacterEncoding,即相当于执行了Response.setCharacterEncoding("UTF-8")这段代码。它保证了服务器端生成的动态内容到达客户端也不会乱码。
但有一种情况下也不会出现乱码,就是如下例这种情况,前提是没有设置Request的CharacterEncoding:
1 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
2 response.getWriter().write(request.getParameter("xxxxx"));
3 }
这种情况下提交过来的表单数据其实是ISO-8859-1的编码,而返回给客户端又没有<%@ page contentType="text/html; charset=UTF-8" %>的设置,所以还是ISO-8859-1的编码,但是为什么没有乱码呢?其实已经乱码了,如果在第2行下断点的话,会发现request.getParameter("xxxxx")的返回值就是乱码。可以用一句Java代码来解释为什么客户端显示结果没有乱码,如下:
1 System.out.println(new String("你好,世界".getBytes("ISO-8859-1"), "ISO-8859-1");
很奇怪这句代码,明明是中文,应该用GB2312或GBK之类的字符集编码来getBytes,却用了ISO-8859-1,事实证明,这种互逆操作对字符串本身没有任何影响,只要getBytes和new String的时候字符集编码是一致的就不会引起乱码。
上面这句代码正好说明了数据从客户端POST到服务器端时是ISO-8859-1编码,然后从服务器端写回到客户端还是ISO-8859-1编码,所以就没有造成乱码,如果这里不是直接写回到客户端,而是forward到另一个JSP页面,而这个页面恰好使用了<%@ page contentType="text/html; charset=UTF-8" %>来设置Response的CharacterEncoding,那么在页面中输出xxxxx还会产生乱码,同样用一句Java代码来解释,如下:
1 System.out.println(new String("你好,世界".getBytes("ISO-8859-1"), "UTF-8"));
所以,最后结论是如果想POST到服务器端不乱码就要设置Request的CharacterEncoding,写回到客户端不乱码就要设置Response的CharacterEncoding,若是JSP页面要设置<%@ page contentType="text/html; charset=UTF-8" %>。
3.AJAX乱码问题(不借助任何JS框架,像Prototype之类的框架会对GET请求的queryString自动应用encodeURIComponent()编码)
GET请求时,需要对queryString使用encodeURIComponent()编码之后再提交到服务器。这是XMLHttpRequest规范所要求的。
POST请求时,不需要使用encodeURIComponent()。
通过对应用程序下断点发现,GET请求和POST请求的数据发送到服务器端都是正常的没有乱码,但是服务器端生成的动态内容写回客户端却是乱码,说明Response的CharacterEncoding设置错误,反过来我们再想一下,我们根本就没有设置过Response的CharacterEncoding,为什么呢?因为我们是以AJAX的方式提交表单,返回后不像JSP页面那样有<%@ page contentType="text/html; charset=UTF-8" %>来设置Response的CharacterEncoding,所以就会出错。
综合上述,解决的办法就是各大网站提出的通用解决方案Filter,如果你的应用没有用到AJAX,只设置Request的CharacterEncoding即可,否则Response的CharacterEncoding也要设置。下面是一个Filter的示例,只引用doFilter方法来说明问题:
1 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
2 request.setCharacterEncoding("UTF-8");
3 response.setCharacterEncoding("UTF-8");
4 chain.doFilter(request, response);
5 }