14 四月 2008

JSP页面编码问题处理

今天使用Filter做过滤器,实现的功能如下:对客户端所有请求进行过滤器编码为GB2312,这样就不用每一个页面添加编码设置,简化开发。出现一个小问题:客户端输入用户名和密码,在服务器验证时发现验证无法通过。



表单如下:

<form action="LoginChecker" method="POST">
<table border="1">

<tr>
<td>用户名称:</td>
<td><input type="text" name="userId"></td>
</tr>
<tr>
<td>用户密码:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td><input type="hidden" name="originalURI" value="${requestScope[originalURI]}"></td>
<td><input type="submit" value="登陆" name="submit"></td>
</tr>
</table>
</form>


过滤器如下:

package com.p268.google;

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* <p>Example filter that sets the character encoding to be used in parsing the
* incoming request, either unconditionally or only if the client did not
* specify a character encoding. Configuration of this filter is based on
* the following initialization parameters:</p>
* <ul>
* <li><strong>encoding</strong> - The character encoding to be configured
* for this request, either conditionally or unconditionally based on
* the <code>ignore</code> initialization parameter. This parameter
* is required, so there is no default.</li>
* <li><strong>ignore</strong> - If set to "true", any character encoding
* specified by the client is ignored, and the value returned by the
* <code>selectEncoding()</code> method is set. If set to "false,
* <code>selectEncoding()</code> is called <strong>only</strong> if the
* client has not already specified an encoding. By default, this
* parameter is set to "true".</li>
* </ul>
*
* <p>Although this filter can be used unchanged, it is also easy to
* subclass it and make the <code>selectEncoding()</code> method more
* intelligent about what encoding to choose, based on characteristics of
* the incoming request (such as the values of the <code>Accept-Language</code>
* and <code>User-Agent</code> headers, or a value stashed in the current
* user's session.</p>
*
* @author Craig McClanahan
* @version $Revision: 500674 $ $Date: 2007-01-28 00:15:00 +0100 (dim., 28 janv. 2007) $
*/


public class SetCharacterEncodingFilter implements Filter {

// ----------------------------------------------------- Instance Variables
/**
* The default character encoding to set for requests that pass through
* this filter.
*/
protected String encoding = null;
/**
* The filter configuration object we are associated with. If this value
* is null, this filter instance is not currently configured.
*/

protected FilterConfig filterConfig = null;
/**
* Should a character encoding specified by the client be ignored?
*/

protected boolean ignore = true;

// --------------------------------------------------------- Public Methods
/**
* Take this filter out of service.
*/
public void destroy() {

this.encoding = null;
this.filterConfig = null;

}
/**
* Select and set (if specified) the character encoding to be used to
* interpret request parameters for this request.
*
* @param request The servlet request we are processing
* @param result The servlet response we are creating
* @param chain The filter chain we are processing
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet error occurs
*/

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {

// Conditionally select and set the character encoding to be used
// System.out.println("ignore="+ignore+",encoding="+this.encoding);

if (ignore || (request.getCharacterEncoding() == null)) {
String encoding2 = selectEncoding(request);
System.out.println("encoding="+encoding2);
if (encoding2 != null)
request.setCharacterEncoding(encoding2);
}

// Pass control on to the next filter
chain.doFilter(request, response);

}
/**
* Place this filter into service.
*
* @param filterConfig The filter configuration object
*/

public void init(FilterConfig filterConfig) throws ServletException {

this.filterConfig = filterConfig;
this.encoding = filterConfig.getInitParameter("encoding");
String value = filterConfig.getInitParameter("ignore");
if (value == null)
this.ignore = true;
else if (value.equalsIgnoreCase("true"))
this.ignore = true;
else if (value.equalsIgnoreCase("yes"))
this.ignore = true;
else
this.ignore = false;

}

// ------------------------------------------------------ Protected Methods
/**
* Select an appropriate character encoding to be used, based on the
* characteristics of the current request and/or filter initialization
* parameters. If no character encoding should be set, return
* <code>null</code>.
* <p>
* The default implementation unconditionally returns the value configured
* by the <strong>encoding</strong> initialization parameter for this
* filter.
*
* @param request The servlet request we are processing
*/

protected String selectEncoding(ServletRequest request) {
return (this.encoding);
}
}

用户验证Servlet如下:

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.p268.google;



import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;


/**
*
* @author lonetown
*/
public class LoginChecker extends HttpServlet {

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String userId = request.getParameter("userId");
String password = request.getParameter("password");
String targetURI=request.getParameter("originalURI");

if ((!userId.equals("张三")) || (!password.equals("1234"))) {
throw new ServletException("认证失败!");
}

HttpSession session = request.getSession();
session.setAttribute("passed", "true");
session.setAttribute("userId", userId);
response.sendRedirect(targetURI);
}
}

部署文件配置如下:

<filter>
<filter-name>setCharacterEncoding</filter-name>
<filter-class>com.p268.google.SetCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>GB2312</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>setCharacterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


结果出现问题:

snapshot2.png
解决方案:

1、先考虑服务器端是否拿到用户数据。

在Servlet中添加两行代码,分别得到用户名和密码:
System.out.println("userId="+userId);
System.out.println("password="+password);
结果如下:出现同样的500错误;但是在命令行下输出
userId=寮??
password=1234

服务器端能够拿到数据。但是发现userId不是张三,而变成乱码。因此乱码和"张三"比较,肯定不相同。所以问题出在过滤器编码上。

2、检测编码:

在Servlet中,在验证数据前添加

……

String password = request.getParameter("password");
String targetURI=request.getParameter("originalURI");
System.out.println("encoding="+request.getCharacterEncoding());
if ((!userId.equals("张三")) || (!password.equals("1234"))) {
……

运行后,命令行输出

encoding=GB2312
所以编码没有问题,所有的请求都被过滤器过滤,编码为GB2312。

但是为何拿出的数据会变成乱码?

我们知道,当一个用户通过JSP页面发送请求的时候,会经历如下过程:

首先用户访问登陆页面,出现登陆的表单。此时用户会发送请求,而服务器会根据用户的请求找到相应的资源,返回给客户端。也就是说,用户所访问的页面,其内容是由服务器端发送的。当用户提交表单时,此时会再次访问服务器,由于过滤器的配置,服务器对任何请求和响应都会进行过滤编码,所以服务器端将表单内容编码为GB2312。回到登陆页面观察,发现登陆页面的编码格式为:UTF-8。此时问题找到,前台JSP页面编码为UTF-8,而到后台服务器端以GB2312解码,自然会是乱码。所以将前台页面改为GB2312,或者将后台过滤器编码设置为UTF-8试试。结果发现正常,能够正确编码。

总结原因如下:

在服务器端存储的用户数据并不是过滤器所设置的编码格式。过滤器设置的编码格式只在数据进行传递时进行编码。前台页面的编码格式为UTF-8,当用户请求该页面时,客户浏览器以UTF-8形式解码,自然看到正确的登陆页面。当用户提交表单数据时,表单数据是以ISO-8859-1的编码方式进行传递,并不是以UTF-8进行传递。JSP页面所设置的编码,仅仅告诉浏览器以什么方式读取,并不是说明所有数据是以UTF-8编码进行传递的,数据传递时有一个默认的编码:ISO-8859-1。所以无论服务器端发送数据,还是客户端发送数据,默认的编码格式都是ISO-8859-1,然后根据本地编码设置,将该编码以本地编码进行解析。但是有一个前提,就是服务器端编码,客户端解码或者服务器端解码,客户端编码,都必须保证两者编码格式相同。即客户端发送数据时以GB2312编码,然后转换为ISO-8859-1进行传递,当服务器端接收到数据时,此时接受的数据为ISO-8859-1,那么必须以GB2312解码,而不能以UTF-8解码。否则仍然乱码。当然,这个过程仅仅针对数据的发送和响应而言的。但是对于服务器端存储的数据而言,它的编码格式会根据服务器的平台为准,只有当数据需要发送给用户时,才会以相应的编码方式编码。

这样,我的问题就找到了。之所以出现我的问题,就在于客户端和服务器端以及过滤器编码设置不一致导致的。我的前台是UTF-8编码,过滤器编码是GB2312。两者编码格式不一样,但是传送的数据都按照各自的方式读取,自然出现问题。所以我将过滤器编码设置为UTF-8,这样,无论客户是发送数据还是读取数据,服务器和客户端编码都是一致的。

没有评论: