问题描述:
在已经在应用filter中指定了页面编码为UTF-8的情况下,通过GET方式发送的请求中的中文依然被识别为乱码,而通过POST方式发送的中文识别正常.
原因:
Tomcat在解析两种不同的请求方式传递的参数时处理出现了问题,应该视为一个BUG,Tomcat 8.0.0以后的版本一定程度上修复了此问题.
代码分析:
Tomcat解析前台传递过来的参数时使用的类为org.apache.tomcat.util.http.Parameters,解析Get传参时调用的方法为
/** Process the query string into parameters
*/
public void handleQueryParameters() {
if( didQueryParameters ) {
return;
}
didQueryParameters=true;
if( queryMB==null || queryMB.isNull() ) {
return;
}
if(log.isDebugEnabled()) {
log.debug("Decoding query " + decodedQuery + " " +
queryStringEncoding);
}
try {
decodedQuery.duplicate( queryMB );
} catch (IOException e) {
// Can't happen, as decodedQuery can't overflow
e.printStackTrace();
}
processParameters( decodedQuery, queryStringEncoding );
}
注意标红的一行,此时字符编码方式由Parameters对象的queryStringEncoding属性指定,若此属性为空,则使用默认编码方式iso8859-1.然而此字段无法在编码时手工指定,是在分配request对象时由Tomcat赋值的.
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
if (request == null) {
// Create objects
request = connector.createRequest();
request.setCoyoteRequest(req);
response = connector.createResponse();
response.setCoyoteResponse(res);
// Link objects
request.setResponse(response);
response.setRequest(request);
// Set as notes
req.setNote(ADAPTER_NOTES, request);
res.setNote(ADAPTER_NOTES, response);
// Set query string encoding
req.getParameters().setQueryStringEncoding
(connector.getURIEncoding());
}
上面的代码为org.apache.catalina.connector.CoyoteAdapter的一部分,标红的部分指出queryStringEncoding应该由Connector给出,但是在Connector的构造中指明了URIEncoding的值为null(特指在Tomcat 8.0.0-RC1版本中),也就造成了所有的以get方式传递的参数均会以iso8859-1格式解码,导致中文乱码.
POST方式不会出现此问题,因为Post的参数解析函数为
public void processParameters( byte bytes[], int start, int len ) {
processParameters(bytes, start, len, getCharset(encoding));
}
此时编码方式由Parameters对象的encoding属性指定,此属性值获取优先级为 (Request的Content-Type中获取) > (用户代码中指定) > (Tomcat指定) ,只要在解码前手工指定过编码方式,就不会出现乱码问题.
解决方法:
1 升级更新版Tomcat,新版本中通过在Connector构造时设定字符编码的方式绕过了此问题.
if (!Globals.STRICT_SERVLET_COMPLIANCE) {
URIEncoding = "UTF-8";
URIEncodingLower = URIEncoding.toLowerCase(Locale.ENGLISH);
}
2 在filter中通过反射方式修改request.request.coyoteRequest.parameters.queryStringEncoding的值为需要的值(request.request不是拼写错误,就是request里的request对象).