实际运用 Tomcat 5.0.19,我们了解在不修改 Tomcat 源代码的状况下,使用者透过 Form submit 的数据将一律以 ISO8859-1 处理,程序设计师必须自行将字符串将转换为 Big5(繁体中文) or GB2312/GBK(简体中文),我们在应用程序中,对所有的 request.getParameter("xx"); 作了 toBig5String() 的处理,理论上,所有的中文问题应该不会出现才对,结果,还是发现某些状况下,中文还是变成乱码!
经过分析整理,我们发现问题出在 QueryString 的解析,以前在 Tomcat 4.x 时代,无论 SUBMIT 时采用 GET or POST,Tomcat server 对 parameters 的处理都采用相同的编码,但在 Tomcat 5.x 版,不知何故,却将 QueryString 的解析独立出来,目前确认,Form 的 Method 采用 GET 及直接将参数写在 URL 上的中文,上传到 Tomcat 时,无论如何转码,都会变成乱码,那怕你事先作过 URLEncode 也一样。
网站上,有人针对这个问题,建议将所有中文改采用 base64 编码,到了 server 上,程序将自行土 base64 decode 回来,确保中文不会发生问题。这样作法当然可以解决这个问题,但是所有网页变成限定要采用 POST,且程序设计师要随时分清楚,那个参数是采用 GET 上传,那个参数是采用 POST 上传,然后再针对不同的方式采用不同的解析,这样的程序一点儿移植性都没有,更别提跨平台、跨国际语言了。
研究 Tomcat 的檔及原始码,我们找到了问题所在及解决的方法,只有按着以下的作法,才能使 Form submit 的资料完全按着 ISO8859-1 的编码,当然,若是全照着 Tomcat 的檔说明去作,肯定还是不行,你还是得加上这个参数到 server.xml 中才行。
解决方案
请先研究 $TOMCAT_HOME/webapps/tomcat-docs/config/http.html 这个说明檔,撷录重点如下:
URIEncoding:This specifies the character encoding used to decode the URI bytes, after %xx decoding the URL. If not specified, ISO-8859-1 will be used.
useBodyEncodingForURI:This specifies if the encoding specified in contentType should be used for URI query parameters, instead of using the URIEncoding. This setting is present for compatibility with Tomcat 4.1.x, where the encoding specified in the contentType, or explicitely set using Request.setCharacterEncoding method was also used for the parameters from the URL. The default value is false.
上述二个 Tomcat 参数,是设定在 server.xml 中的 http <Connector /> 区块,要解决 QueryString 中文变成乱码的问题,你必须至少设定这二个参数其中之一。
URIEncoding 请设定为 URIEncoding="ISO-8859-1" 指定为 "ISO-8859-1" 编码,让 QueryString 的字符编码与 post body 相同。
useBodyEncodingForURI 这是用来相容 Tomcat 4.x 版的,设定的值是 "true" or "false",意思是指 "要不要让 QueryString 与 POST BODY 采用相同的字符编码 ?",若是设成 true,那也可达到 "ISO-8859-1" 编码的需求。
建议,采用 URIEncoding 的设定,毕竟 useBodyEncodingForURI 的作法是为了兼容 Tomcat 4.X。不过若照原文的说明,理论上这二个参数都不设,Tomcat 也该采用 "ISO-8859-1" 的编码,那为什么还是会有问题呢 ? 我们由 Tomcat Source Code 来看就清楚了。
// 这一段代码是 Tomcat 用來解 QueryString 的,
// 在 org.apache.tomcat.util.http.Parameters 这个类中。
private String urlDecode(ByteChunk bc, String enc)
throws IOException {
if( urlDec==null ) {
urlDec=new UDecoder();
}
urlDec.convert(bc);
String result = null;
if (enc != null) {
bc.setEncoding(enc);
result = bc.toString();
}
else {
CharChunk cc = tmpNameC;
cc.allocate(bc.getLength(), -1);
// Default encoding: fast conversion
byte[] bbuf = bc.getBuffer();
char[] cbuf = cc.getBuffer();
int start = bc.getStart();
for (int i = 0; i < bc.getLength(); i++) {
cbuf[i] = (char) (bbuf[i + start] & 0xff);
}
cc.setChars(cbuf, 0, bc.getLength());
result = cc.toString();
cc.recycle();
}
return result;
}
请特别注意红色区块,当 Tomcat 发现 QueryString 并没有设定 encode 时,并非像文件中所说预设采用 ISO-8859-1 的编码,而是用一段 fast conversion 来处理,才会造成中文问题,所以,还是必须在 Server.xml 中,加上 URLEncoding 的参数设定才行哦。
Connector 的设定范例:
<Connector
debug="0"
acceptCount="100"
connectionTimeout="20000"
disableUploadTimeout="true"
port="80"
redirectPort="8443"
enableLookups="false"
minSpareThreads="25"
maxSpareThreads="75"
maxThreads="150"
maxPostSize="0"
URIEncoding="ISO-8859-1"
>
</Connector>
一般说来,我们在使用 Tomcat 4 透过 GET or POST 的方式传参数时,通常都是使用 Filter 的方式来解决中文传参数的问题。 但是到了 Tomcat 5.0.19 之后,解决中文传递参数时,就必须考虑是使用 GET or POST,两种解决的方式不一样。
如果是使用 GET 的方式传递时,就如同 精灵 兄 的文章所述,或者使用
String name = new String((request.getParameter("name")).getBytes("ISO-8859-1"),"Big5");
若是使用 POST 的方式時,就延用傳統一般解決中文的方式
request.setCharacterEncoding("Big5");
不过当初我最后的做法是使用 Filter 的方式 。
Filter 的做法就是:先判断是使用那种传递方式( GET or POST),假若是用 GET 的方式就采用第一种 code;若使用POST 方式,就采用第二种 code。