关于JSP乱码问题的初步理解和解决
最近开始接触J2EE技术,碰上了JSP传递中文时乱码的问题。其实这个问题已经是被说烂了,但是还是动手尝试解决了一下。本文藉此记录一下解决过程和解决方法。
还是首先描述一下问题。最开始的时候,我从数据库里面读取中文的字符串用户名到显示信息的JSP页面,发现了显示传入的参数值的部分显示乱码,而英文一切正常,JSP本身输出的中文也正常。由此基本可以确定,问题出在参数传递的传递过程中。
解决问题从简单的开始。为了明确地进行测试,我新建了一个web工程,并加入了两个JSP页面,一个送出中文字符串参数(简单命名为One.jsp),另一个接受参数并输出(Two.jsp)。
Two.jsp很简单地这样写:
<%@ page language="java"import="java.util.*" pageEncoding="gbk"%>
<html>
<head>
</head>
<body>
<%
String name = request.getParameter("name");
%>
我还是<%=name %> <br>
</body>
</html>
我的两个JSP出了body外的其他部分,一般就不做改变了,就按照这个格式写。
这时候要写One.jsp了,第一个问题出现了,到底参数传递的问题来自数据库数据的调用还是单纯的页面传递呢?于是我先看一下数据库设置。我用的数据库是mysql,在数据库命令窗口里输入:showvariables like '%char%';
结果得
+--------------------------+--------------------------------------------------------------+
| Variable_name | Value |
+--------------------------+--------------------------------------------------------------+
| character_set_client | gbk |
| character_set_connection | gbk |
| character_set_database | gbk |
| character_set_filesystem | binary |
| character_set_results | gbk |
| character_set_server | gbk |
| character_set_system | utf8 |
| character_sets_dir |C:\Program Files (x86)\MySQL\MySQL Server 5.5\shar\charsets\ |
+--------------------------+--------------------------------------------------------------+
8 rows in set (0.52 sec)
这里可以看到mysql的编码设置已经是gbk,可以识别中文了。这个因为我有过经验教训,所以设置的时候就已经设置为gbk了,这个可以比较方便地调用mysql的向导进行设置,这里不赘述。
虽然看到了数据库的编码是gbk,但是还是不是很放心,还是在jsp里面查询一下试试,在One.jsp里面照以下写:
<%
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/usermanage","root","123");
Statement sta = conn.createStatement();
//这里查询id=9,是因为我的user表里面刚好第9个是中文用户名
ResultSet rs = sta.executeQuery("select * from user whereid=9");
String name = "";
if(rs.next())
{
name = rs.getString("name");
}
%>
<%=name %> <br>
<a href="Two.jsp?name=<%=name%>">Two</a>
这样进行测试,结果是在One.jsp中显示的name正确显示为中文“丁一”,跳转到Two.jsp后,页面显示为乱码,但是url给了有用的信息。Chrome和IE显示的url分别是Two.jsp?name=%B6%A1%D2%BB和Two.jsp?name=丁一。显然,无论是Chrome的%+十六进制数还是IE给出的直接中文,都说明了从One.jsp传递到Two.jsp的数据是符合gbk编码的。
这里就说明了两个问题,一是Two.jsp对传入的参数解码出了问题,而非One.jsp的输出问题,二是本问题纯粹属于JSP页面的问题,而非数据库查询所得的编码问题。
对于One.jsp,除了头部的pageEncoding="gbk"外没有其他地方控制输出编码,所以基本可以断定pageEncoding="gbk"是可以有效控制输出编码的。
这里有一篇文章写JSP字符集写得比较全面,值得记录并推荐一下:http://blog.163.com/slq_0352/blog/static/5902709720083241275783/
之后又试了一下不从数据库读取数据,而直接在One.jsp中定义中文字符串作输出,上述结论继续成立,按下不表。
JSP的解码问题如何解决呢?继续查资料,得出几个方法,以下是比较直接的两个方法:
1、在所有request.getParameter()之前插入request.setCharacterEncoding("gbk");,将request传进的参数全部设置识别为gbk。
2、进行转码解释,修改为这样获取传入参数:String name = newString(request.getParameter("name").getBytes("iso8859_1"),"gbk");
这里有一点值得注意的是iso8859_1是默认的解码方式。这么一来,乱码的直接原因就呼之欲出了。传进来的是一个gbk编码的数据串,而JSP页面却用iso8859_1去 识别,自然是识别不出正确的字符。事实上,众所周知iso8859_1连中文字符都压根没有
之后分别对两种方法进行测试。测试结果是第二个方法是可行的,转码之后可以正确在页面上输出中文字符,但是第一种方法是死活没有达到效果。但是在网上很多人都表示第一种方法可行的,如此言之凿凿,也不像是信口开河。第一个方法是否可行,确实让我苦恼了不少时间,不过值得欣慰的是JSP乱码的问题算是找到了一个可行的解决方案,setCharacterEncoding的问题暂且也放一放。
接下来考虑另一个问题,每一次都用这样转码,随着页面增多和参数增多,也是一件很烦人的事。为此我注意到编译过滤器的方法。通过设置过滤器,可以将页面之间流通的数据进行处理,每一次有页面之间数据传递的时候,过滤器就会被调用,通过过滤器完成编码的处理,让代码更加简洁。基本的过滤器作为一个servlet出现,具体是这样写的:
public class GBKFilter extends HttpServletimplements Filter {
publicvoid doFilter(ServletRequest request, ServletResponse response,
FilterChainchain) throws IOException, ServletException {
/*
/添加相应处理内容
*/
chain.doFilter(request, response);
}
}
Filter接口还有其他函数,具体可以去翻翻API文档,不赘述。
写完过滤器的servlet之后还没完,还要到工程对应的web.xml中添加一段部署,让管理容器知道你添加了一个过滤器。添加内容如下:
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>GBKFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
OK,上面的方法和设置都没问题,接下来要写doFilter方法里面的那个“添加相应处理内容”。我开始是这样写的:
public void doFilter(ServletRequestrequest, ServletResponse response,
FilterChainchain) throws IOException, ServletException {
if((request.getCharacterEncoding()==null)){
//这个encoding在类中定义,内容为编码名称,这里我初始化为”gbk”
String encoding = this.encoding;
if(encoding!=null){
request.setCharacterEncoding(encoding);
}
}
chain.doFilter(request, response);
}
简简单单就是一个setCharacterEncoding。一试,没有效果。为啥?突然想起来上面那个被我搁置的方法一失效的问题还没有解决,这里用过滤器调用同样的函数,自然也不会有效。于是我将处理手段改成转码,如下:
public void doFilter(ServletRequestrequest, ServletResponse response,
FilterChainchain) throws IOException, ServletException {
if((request.getCharacterEncoding()==null)){
//这个encoding在类中定义,内容为编码名称,这里我初始化为”gbk”
String encoding = this.encoding;
if(encoding!=null){
Enumeration<String>names = req.getParameterNames();
String values[] = null;
while(names.hasMoreElements())
{
String name = names.nextElement();
values = req.getParameterValues(name);
for(int i=0;i<values.length;i++)
{
values[i] = this.toGBK(values[i]);
}
req.setAttribute(name, values);
}
}
}
chain.doFilter(request, response);
}
private String toGBK(String src)
{
Stringstr = null;
try{
str= new String(src.getBytes("iso8859_1"),"gbk");
}catch (UnsupportedEncodingException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
returnstr;
}
toGBK方法就是一个简单的转码。doFilter的操作从直接设置request的编码类型,改为对参数逐个调用toGBK方法转码为gbk,并且回写。如此以来,问题得到进一步的解决,其他文件在传递参数的时候无需手动转码了,变得更为简单。
不过,到此时,依然还有一个问题:setCharacterEncoding()的效果在哪儿呢?总不会完全没用吧?继续查资料,发现有人说在表单传递的时候,setCharacterEncoding()用于对post方法的参数传递上,在get方法的参数传递无效。我开始对此并无留意,因为我至此为止的所有参数输出都是以超链接设置url后部为?name=”XXX”的形式进行的,并未涉及表单数据。莫非其中有猫腻?
果断试一试,在One.jsp中插入了一个表单,设置为post方法提交。果然,setCharacterEncoding方法起到作用了。于是换位get方法,setCharacterEncoding失效了。由此得出一个猜想,以超链接设置url后部的形式传递参数,与表单提交中的get方法相类似。这一点的验证暂时没有更深入的探讨,先保留一下。
最后,为了使得过滤器更加完整,补充了对参数传递方法的判断,并作出对应的不同操作,具体代码如下:
public void doFilter(ServletRequestrequest, ServletResponse response,
FilterChainchain) throws IOException, ServletException {
HttpServletRequestreq = (HttpServletRequest)request;
HttpServletResponseres = (HttpServletResponse)response;
Stringmethod = req.getMethod();
if(method.equalsIgnoreCase("post"))
{
req.setCharacterEncoding("gbk");
}
else
{
Enumeration<String>names = req.getParameterNames();
Stringvalues[] = null;
while(names.hasMoreElements())
{
Stringname = names.nextElement();
values =req.getParameterValues(name);
for(inti=0;i<values.length;i++)
{
values[i]= this.toGBK(values[i]);
}
req.setAttribute(name,values);
}
}
res.setContentType("text/html;charset=gbk" );
chain.doFilter(req, res);
}
以上~~~~