|  
 5.        使用过滤器 
实际中我们处理客户端数据时,大多数时候都是在JavaBean中实现的,我们当然可以在会话Bean中把decoding掺合进去,但没有谁愿意这么做,而事实上我们可能会有很多的Bean,这种做法是维护和更新所不能容许的。我们还可以在JSPs/Servlets中通过ServletRequest.setCharaterEncoding(String encoding)来设置,但在众多JSPs/Servlets中这么做也是件令人讨厌的事。最理想的办法是一劳永逸——只在一个地方进行编码和解码的处理,那就是在过滤器(Filter)中。我们只要在过滤器中对客户提交的数据正确解码了,就不用JSPs/Servlets/JavaBeans来操心了。我们的客户可能是大陆的,也可能是台湾省的,也就是说我们至少还得为区分GB2312和Big5来操心了,最理想的就是让它们提交上来的数据都是UTF-8编码的,那就不用决定decoding了,这里我们可以通过第四种方法来让服务器影响浏览器选择encoding,即设置响应头中的Content-Type,让所有的JSP和Servlet的响应实体的编码都是UTF-8,那么浏览器也就会选择我们所使用的UTF-8来编码提交的数据。 
package filters; 
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; 
import javax.servlet.http.HttpServletRequest; 
  
public class EncodingFilter implements Filter 
{ 
       private FilterConfig config; 
       private String defalutEncodeing; 
       public void init(FilterConfig config) 
       { 
              this.config = config; 
              defalutEncodeing = config.getInitParameter("encoding"); 
       } 
       public void doFilter(ServletRequest request, ServletResponse response, 
                                           FilterChain chain)        throws IOException,ServletException 
       { 
              request.setCharacterEncoding(defalutEncodeing); 
              String uri = ((HttpServletRequest)request).getRequestURI(); 
              if(uri.indexOf(".jsp") != -1 || uri.indexOf("servlet") != -1) 
                     response.setContentType("text/html;charset=" + defalutEncodeing); 
              System.out.println("Filter set the encoding of the response to " +  
                                             response.getCharacterEncoding()); 
              chain.doFilter(request, response); 
       } 
       public void destroy() 
       { 
              //... 
       } 
} 
在Context的web.xml中进行如下配置: 
  <filter> 
    <filter-name>Encoding Filter</filter-name> 
    <filter-class>filters.EncodingFilter</filter-class> 
    <init-param> 
      <param-name>encoding</param-name> 
      <param-value>UTF-8</param-value> 
    </init-param> 
  </filter> 
  
  <filter-mapping> 
    <filter-name>Encoding Filter</filter-name> 
    <url-pattern>/*</url-pattern> 
  </filter-mapping> 
可用一个test.jsp测试却发现,浏览器的输出并没有像我们预想的那样好,test.jsp源代码如下: 
<%@ page pageEncoding="GBK"%> 
<%String str = "在JSP中,编码已经被过滤器设置成:" + response.getCharacterEncoding(); 
System.out.println(str);%> 
<%=str%> 
它的输出如图3-3和图3-4,在客户端出现了乱码是没办法的事,因为通过服务器的输出我们可以发现在过滤器中,编码的确已经被设置成UTF-8,但在JSP中,编码又被设置成了ISO-8859-1。用过滤器对JSP编码设置失败,所以JSP的输出也失败了,我们看到了乱码。 
  
图 3-3 对过滤器对JSP文件预设置编码失败 
  
 图 3-4 服务器输出说明在JSP中编码又被设置为ISO8859-1 
但一段Servlet代码却证明了过滤器本来已经成功进行了设置。 
import javax.servlet.http.HttpServlet; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import javax.servlet.ServletException; 
import java.io.IOException; 
import java.io.PrintWriter; 
public class encoding extends HttpServlet { 
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
    throws ServletException, IOException { 
              PrintWriter out = response.getWriter(); 
              String str = "在Servlet中,编码是:" + response.getCharacterEncoding(); 
              System.out.println(str); 
              out.println(str); 
              out.flush(); 
    } 
} 
你可以在有过滤器和没有过滤器的情况下做实验,结果应该发现在有过滤器状态下,服务器端和客户端均正确得到了UTF-8的输出,在没有过滤器的状态下,服务器端正确输出编码为ISO-8859-1,客户端却输出了乱码,不管是JSP时的乱码还是无过滤器Servlet的乱码,这些都是可以理解的,因为当没有对JSP明确设置page的contentType指令,JSP引擎自动把它设置为ISO-8859-1,而用ISO-8859-1来编码中文,当然只有得到乱码了。 
现在可以说利用过滤器来设置响应实体编码,以达到控制浏览器提交数据的编码的希望是没有意义了,没有谁会用Servlet来输出含Form的HTML页。但在过滤器中设置用户提交的数据的编码码还是有意义的。对于JSP我们还是可以妥协到在每个JSP中来定义contentType,也许在一JSP文件中定义contentType,而其他JSP文件对这个文件使用静态包含是可行的。 
<%--encoding.jsp--%> 
<%@ page contentType=”text/html;charset=UTF-8”%> 
静态包含 
<%--form.jsp--%> 
<%@ page pageEncoding="GBK"%> 
<%@ include file="encoding.jsp"%> 
<%String name = request.getParameter("name");%> 
<html> 
  <head> 
    <title>Form</title> 
  </head> 
  <body> 
    <form method="POST" action="test.jsp"> 
      <label>请输入您的姓名</label> 
      <input type="text" name="name" size="20"> 
      <input type="submit" value="提交"> 
    </form> 
<%if(name != null && !name.equals("")){ 
      //%><p>您的姓名是:<%=name%></p><% 
}%> 
  </body> 
</html> 
在这里,我们对客户提交的数据的解码是在过滤器Encoding Filter中完成的。form.jsp中的<%@ page pageEncoding="GBK"%>是不能省略到encoding.jsp中去,很明显它只对使用了它的JSP文件有效,而且不会在include指令中传递到包含文件中去的。 
6.        URI的中文字符串 
通常情况下,我们都会尽力避免在URI(Uniform Resource Indentifier,统一资源标识符,定义在RFC2396中)中出现非英文字符,但不是所有的时候都能避免,而且这种避免可能加大我们的开发成本或运行效率。如果想直接用含中文的URL(Uniform Resource Locator,统一资源定位符,定义在RFC1738中,是URI的子集)对服务器上的Web资源进行访问,那是不行的。比如: 
http://localhost/我是中国人.html 
是不能访问到服务器上的“我是中国人.html”,因为浏览器(我使用的是MSIE6.0b)会无条件对该URL用UTF-8来编码: 
http://localhost/%E6%88%91%E6%98%AF%E4%B8%AD%E5%9B%BD%E4%BA%BA. html 
而服务器却用系统缺省编码来解码这个URL,即相当于执行了 
java.net.URLDecoder.decoder(url); 
这个动作。不过这个方法已经不被赞成使用了(Deprecated),而应该使用新的方法 
   public String decode(String url, String charset) 
    服务器使用缺省的GBK来解码URI,而浏览器却很难做到使用GBK来编码URI,JavaScript中有三个用来编码URI的全局函数 
l          encodeURI(uri) 
l          encodeURIComponent(uri) 
l          escape(uri) 
前面两个函数出现在IE5.5+中,它们用UTF-8来对参数uri编码,并返回编码的字符串;最后那个函数已经不被赞成使用了(Deprecated),它直接使用“%”加字符的Unicode内码来表示字符,如 
escape(“我是中国人”)=%u6211%u662F%u4E2D%u56FD%u4EBA 
这样,我们只好借助java.net.URLEncoder来编码URI了,如 
<%-- encodeURL.jsp--%> 
<%String file = “我是中国人.html”; 
  url = java.net.URLEncoder.encode(file, "GBK");%> 
<a href=”<%=url%>”><%=file%></a> 
这样,我们就实现了访问文件名中含中文字符的Web文件。其实这种即增加开发成本,又牺牲服务器效率的做法是没有多大意义的,没有谁会故意非用个中文文件名不可。 
在HttpServletRequest中,请求查询字符串(通过方法getQueryString()获得)即不是URI(通过方法getRequestURI()获得)的一部份,也不是URL(通过方法getRequestURIL()获得)的一部份,服务器使用URLDecoder解码URL时,丝毫不对它产生影响,可以说它是被独处理的,而我们前面所使用的过滤器Encoding Filter里面的 
request.setCharacterEncoding(defalutEncodeing); 
却会对它产生影响。让我们来看一个实验,该实验加载了过滤器Encoding Filter: 
<script> 
//浏览不会自动编码查询字符串中的非英文字符 
function encodingHref(obj) 
{ 
    obj.href = encodeURI(obj.href); 
} 
</script> 
<a href="test.jsp?name=胡洲" onclick="encodingHref(this)">go</a> 
test.jsp的源代码如下: 
<%@ page contentType="text/html;charset=GBK"%> 
pathInfo = <%=request.getPathInfo()%><br> 
pathTranslated = <%=request.getPathTranslated()%><br> 
contextPath = <%=request.getContextPath()%><br> 
queryString = <%=request.getQueryString()%><br> 
requestURI = <%=request.getRequestURI()%><br> 
requestURL = <%=request.getRequestURL()%><br> 
servletPath = <%=request.getServletPath()%><br> 
name = <%=request.getParameter("name")%> 
输出如图3-5: 
  
图3-5  
输出的结果是我们希望的。我们还可以在服务器端使用JSP代码: 
<a href=”test.jsp?name=<%=URLEncoder.encode(“胡洲”, “UTF-8”)%>”>go</a> 事先编码好请求的URI,使用JavaScript代码编码查询字符串和使用JSP代码比起来,两者各有所长。 
 
  |