,欢迎光临!
加入收藏设为首页请您留言
您当前位置:网站首页 >> 西北地区 >> 用SERVLET过滤器简化中文的输入和输出-上海联兵环保免费电话:400-600-5030

用SERVLET过滤器简化中文的输入和输出-上海联兵环保免费电话:400-600-5030

2011-09-13 14:10:29 来源:中国过滤器网 浏览:1

摘要:分析了SERVLET2.3规范新增加的过滤器工作原理,根据过滤器的工作原理和Java对中文编码的处理提出了解决WEB应用程序正确接收、显示中文的有效方法。同时也为软件国际化提供了一个很好的解决方法。
关 键 词:J2EE规范;SERVLET过滤器;UNICODE;UTF8;GB18030
中图分类号:TP393.2   文献标识码:A
1 引言
在以SERVLET为基础的WEB应用程序中,会碰到不能正确地输入、输出中文的问题,原因在于大多数运行SERVLET的容器没有考虑多语言的情况。所以要解决中文的输入、输出问题就要自己动手编写程序。这样,“解决问题”的代码充斥应用程序的各个SERVLET中。如果整个应用程序要移植到别的语言平台,那么大量的代码要修改,导致程序可维护性降低。
2 SERVLET过滤器介绍
SERVLET是J2EE技术规范中的一部分,在SERVLET过滤器引入以前,J2EE结构的典型中间层(WEB层)由WEB/应用服务器(或两者)组成。该应用服务器提供WEB内容服务,并启动SERVLET/JSP回复客户的请求。流程如图1所示。
SERVLET过滤器(SERVLET FILTER)是SERVLET2.3规范中加入的新功能,不仅能完成普通SERVLET的功能,它更重要的任务是在HTTP请求和响应到达指定目的之前,过滤器可以查看和修改它们,其中过滤器本身也可以按照特定的顺序链接在一起。这里资源可以是动态的也可以是静态的。
从图2可以看到,过滤器置于请求/响应处理管道中,应用程序服务器和客户端之间。过滤器为程序员提供了访问请求/响应信息的程序和途径。在使用过滤器之前,还没有办法可以使网络应用程序参与到实际的请求/响应处理中来。WEB应用程序仅仅提供支持和定义被服务的资源。如果应用程序需要访问正在进行操作的请求/响应处理管道,就要借助不可移植的服务器扩展。

过滤器工作流程是客户端发出请求,在请求到达最终的资源处理器前,将依次通过过滤器链,这个操作过程称为入站。入站旅行的最终方向是资源处理器。请求信息在过滤器链中可以被修改,同时可以根据条件使请求不发往资源处理器。另一方面,资源处理器完成了对资源的处理后,响应信息将逆流旅程,这个过程称为出站。
响应信息会跟入站的方向相反,经过每一个过滤器,其目标指向客户端。同样,在出站过程中,用户可以修改响应信息,从而完成一定的任务。
3 如何实现一个过滤器
根据SERVLET2.3规范,每个过滤器类必须实现javax.servlet.filter接口。这个类必须实现的3个方法是:
(1) init(filterconfig)方法:是过滤器被应用服务器实例化时首次执行的方法,负责对过滤器的初始化。用户自定义过滤器的初始化工作要在这里完成,比如读入外部参数等等;
(2) destroy()方法:是过滤器实例从应用服务器中被移除时执行的方法。这里执行释放资源的动作;
(3) doFilter(servletRequest,servletResponse,filterChain)方法:是过滤器的主方法,过滤器需要完成的功能基本上由该方法完成。每当应用程序请求被处理时,应用程序服务器都将调用该方法。
还要注意的一点是filterChain.doFilter(servletRequest,servletResponse)方法,通过在doFilter(servletRequest,servletResponse)方法中调用。filterChain.doFilter (servletRequest,servletResponse)方法可以把请求传向下一个过滤器(如果有的话)或资源处理器。在filterChain.doFilter(servletRequest,servletResponse)方法之前的代码是处理请求信息的,之后的代码是处理响应信息的。
4 SERVLET的中文问题
4.1 几种常用编码:
(1)ISO8859-1: ASCII码只使用了8位(单字节)中的7位,因为在ASCII中第一位都是0,为了扩展字符集,使用第一位为1的方式可以增加128个字符以满足类似拉丁语字母的多数语言的需要。ISO8859-1编码方式的前128个字符与ASCII完全一样,其扩展了ASCII的字符集,这种字符集是一般英文操作系统的默认字符集。
(2)GB2312-80:是我国国家标准汉字信息交换用编码,通行于中国大陆地区及新加坡,简称国标码。两个字节中,第一个字节(高字节)的值为区号值加32(20H),第二个字节(低字节)的值为位号值加32(20h),用这两个值来表示一个汉字的编码。该字符集是几乎所有的中文系统和国际化的软件都支持的中文字符集,是最基本的中文字符集。其编码范围是高位0xa1~0xfe,低位也是0xa1~0xfe;汉字从0xb0a1开始,结束于0xf7fe。它是一般中文操作系统的默认字符集。
(3)GBK:是GB2312-80的扩展,是向上兼容的。它包含了20902个汉字,其编码范围是0x8140~0xfefe,剔除高位0x80的字位。其所有字符都可以一对一映射到Unicode2.0,也就是说Java实际上提供了GBK字符集的支持。这是现阶段Windows和其它一些中文操作系统的缺省字符集,但并不是所有的国际化软件都支持该字符集。
(4)Unicode:是用两个字节表示一个字符的编码格式,其类似ISO8859-1通过增加一个高阶位来扩展ASCI-I。Unicode通过增加一个高位字节来扩展ISO8859-1,如果高位字节是0(00000000)那么低位字节字符与ISO8859-1字符相同,所以如果将高位字节去除可以近似地将Unicode转换为ISO8859-1,这种近似对英语完全没有问题,所以许多文本类都做了这种假设(System.out.PrintStream),但这只有在所有文本都是ISO8850-1时才可以这样做,所以这是经常出现乱码的原因之一。它是Java的内码(包括Java内核以及Java .Class文件)的编码方式。
(5)UTF8:Java如果把字符和转义符都翻译成16位字符的Unicode,那么当操作的文本基本都是规则的使用7位ASCII字符时就显得效率很低,浪费了大量的存储空间。因此Java字节码内的字符串用了一种中间格式———Universal Character Set Transformation Format 8-bit (UTF8)的形式。
4.2 Java如何输出汉字
Java面世以来就面向国际化,Java内码是用Unicode表示的,但Java从源代码的编译,到Class文件的执行,再到运行结果的输出,每一步都存在编码转换的问题。任何一步出错,都不能得到正确的结果。下面研究从源文件编译到Class文件再到执行的过程。
Javac sourcecode.java:javac按照本地平台的编码读取sourcecode.java,然后转换成Unicode/UTF8格式。为了和UNIX的文件系统兼容,编译成的Class文件以UTF8编码格式保存在文件系统中。
Java soucecode:执行时,把UTF8编码格式转成Unicode的编码格式装入虚拟机中。执行输出时把Unicode编码转成本地编码格式输出。
下面以“中”字举例来说明问题。String str =“中”,用记事本编辑并保存。在中文Windows环境下“中”的编码是0xD60xD0(GB2312编码),编译后的Class文件其编码是0xE40xB80xAD(UTF8编码),执行时在内存中的编码是0x4e0x2d(Unicode编码),把该字符串输出到屏幕或者文件中时,如果不指定输入的编码格式,那么默认是本地的编码格式。在中文Windows环境下,通过这种方式编译、执行Java桌面应用程序不会出现任何问题。
然而在Java应用于WEB方面时却出了问题。原因出在最后输出的那一步。SERVLET不知道用户的浏览器用的什么语言,所以它默认按ISO8859-1的字符编码格式输出。也就是说,它把“中”字在内存中的内码0x4e0x2d当成是某个(几个)ISO8859-1的字符的Unicode表示,然而反过来从编码为Unicode码为4e2d的字符得到ISO8859-1的字符的时候,因为找不到相应的ISO8859-1编码的字符,就得到编码为0x3f的ISO8859-1字符。所以输出的中文字显示为一个问号(?)。
解决办法:通过在每一个浏览器的页面加上强制页面响应的编码语句response.setContentType(″text/html;charset=GB2312″);这样SERVLET在输出时就会按指定的字符集输出。
4.3 Java接收汉字
看看由浏览器的输入带来的问题。从浏览器输入有两种方式:用户数据从url中的查询字符串部分输入;用户数据从表单中输入。
先看来自url中的数据。由于url的编码规则规定出现在url中的汉字必须要编码才能输入。Java提供了url编码的函数URLEncoder.encode(“中”),返回的字符串是%D6%D0(其中D6D0是“中”字的GB编码);相反,通过URLDecoder.decode(“%D6%D0”)解码,得到字符“中”。这样通过URL编码的字符串发到浏览器,从浏览器传回来时,因为浏览器发送的URL默认都是以UTF8格式编码,所以%D6%D0以UTF8格式发回来。与SERVLET输出一样,SERVLET容器不知道浏览器的语言设置,所以默认以ISO8859-1编码形式接收传入的URL,它把%D6和%D0分别当作一个字符,这样就造成了URL输入的错误。
再看来自表单中的输入。还是以“中”字为例,用户在要提交的输入域中输入“中”字,然后提交。浏览器把“中”字的GB码D6D0向SERVLET容器发送,SERVLET默认还是以ISO8859-1的编码形式接收表单数据,这样把0xD60xD0作为两个字符保存,造成数据输入错误。到此,可以看出,要解决中文的正确输入问题就要把GB码转成Unicode码,在每个SERVLET中加入request.setCharacterEncoding(″GB18030″)就可以告诉SERVLET按照正确的编码读取输入字符流。但这里要注意的是有的SERVLET容器对HTTP头部的信息始终按ISO8859-1编码来处理,那么针对这种情况,还是要对来自URL中的数据自己动手编码。
4.4 关于JSP
JSP是特殊类型的SERVLET。JSP在被请求时,由SERVLET容器编译成SERVLET,其编译过程和编译SERVLET源文件是一样的。需要注意的是JSP源文件的默认编码方式是用pageEncoding指令表示的。
从上面的讨论可以看出,要同时正确解决SERVLET中的汉字输入和输出的问题,必须要在每一个SERVLET中动手编写转换代码。下面提出一个用过滤器来解决汉字输入输出的方法,仅仅在一个地方编写代码就可以解决所有的问题。
5 一个处理输入/输出编码的过滤器
首先定义自己的请求对象:filter.CharacterRequestWrapper.java
package filter;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.*;
public class CharacterRequestWrapper extends HttpServletRequestWrapper {
String encoding = null;
public CharacterRequestWrapper(HttpServletRequest inReq,String env)
{
super(inReq);
encoding = env;//GB18030
}
public String getParameter(String paramName){
String tmpstr = super.getParameter(paramName);
String tmp =null;
if(tmpstr == null)
return null;
else {
try{
tmp = new String(tmpstr.getBytes(″ISO8859-1″),encoding);
}catch(java.io.UnsupportedEncodingException e){
System.out.println(″error″);
}
}
return tmp;
}
}
其次定义过滤器类:CharacterFilter.java
package filter;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.*;
public class CharacterFilter implements Filter {
private FilterConfig filterConfig = null;
//过滤器初始化
public void init(FilterConfig filterConfig)
throws ServletException {
this.filterConfig = filterConfig;
}
//过滤器回收时执行的操作
public void destroy() {
this.filterConfig = null;
}
//过滤器主体
public void doFilter( ServletRequest request,
ServletResponse response, FilterChain chain )
throws IOException, ServletException {
CharacterRequestWrapper myReq = null;
if(filterConfig==null)
return;
Locale local = request.getLocale(); //注意这里的本地对象,可以根据这个对象//来判断用户浏览器使用的什么语言从而决定使用什么编码方式。
String language =“ISO8859-1”; //默认是英语
If(local.getLanguage().equals(“zh”)) //如果是中国,则编码是GB18030
Language =“gb18030”;
myReq = new CharacterRequestWrapper((HttpServletRequest)request,language);
response.setCharacterEncoding(language);
chain.doFilter(myReq,response); //把请求传向下一个过滤器或资源处理器
}
}
WEB.XML配置文件片断

Char Filter
filter.CharacterFilter


Char Filter

/*

上述代码在TOMCAT5下运行验证通过。
6 结束语
通过使用SERVLET过滤器,达到了只做一次设置就能让所有的SERVLET正确地显示和输入汉字的效果,简化了代码,提高了程序的维护性,同时也为软件国际化提供了一个可行的方法。

 

上海联兵环保科技有限公司
地址:上海市松江区工业区茸北分区茸阳路69号
总机:021-51691929
传真:021-57784244
免费电话:400-600-5030
技术支持:13641659499
E-mail:zhanglianbing@126.com
http://www.shlbhb.com

发表评论
网名:
评论:
验证:
共有0人对本文发表评论查看所有评论(网友评论仅供表达个人看法,并不表明本站同意其观点或证实其描述)
赞助商链接
关于我们 - 联系我们 - 咨询联兵