关于JSP乱码问题的初步理解和解决

关于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);

       }

 

以上~~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值