这两天写一个项目遇到一个很怪异的现象,就是在url中汉字传值,如果是奇数个汉字则出现编码错误。先说下环境:前台页面试gb2312编码,后台的filter是gbk,web server是nginx+resin,nginx用的是操作系统编码,编码是gbk,在输入http://www.我的域名.com/search/searchByName.action?query=%C1%AC%C1%AC%BF%B4 (1) 时后台不需要做处理,
返回字符串正常,再输入http://www.我的域名.com/search/searchByName.action?query=连连看 (2) 时后台就显示乱码。
我分别用chrome、firefox、ie8做了测试,结果虽然是乱码还各有特色。
最初,在网上搜索解决方案,不过大部分都是一个来源,也就是让你在页面上先对汉字url encode一下然后再传值,但是这种情况不符合我的需求,我是要在浏览器中直接输入url,汉字传值。
解决方案一,在nginx中配置,将传递过来的url进行重写,让参数以重叠的方式向后台传送,比如原来是"query=你好啊",重写为"query = 你好啊你好啊",这样可以避免奇数个乱码问题,不过这有一个问题,就是和web server的耦合性太高,只要有汉字传值,就得重写nginx配置文件,不可取。
解决方案二,在后台发现,取出的parameter在utf8转换成gbk时,如果字符串.getbytes()是奇数个,最后一个byte会被吞掉,信息损失了,就没法转换回来了。很烦人,这时候在同事的启发下,发现request.getQueryString(),也可以得到参数,并且都是被转码过的,形如“query=%e7%be%8e%e5%a5%b3”,这个是没有被gbk过滤的,所以就通过处理这个字符串获取到了没有信息损失的编码。不过这时还有个问题,页面输入(1)(2)两个链接时后台要拿到相同的汉字,但是他们被编码后显示的编码值却不同,就是这个时候只要通过判断是否为utf8的url(此时有汉字传值),如下代码:
String parameterString = request.getQueryString();
if(parameterString!=null&&!parameterString.equals("")){
try {
CharTools ct = new CharTools();
if(ct.isUtf8Url(parameterString)){
parameterString = URLDecoder.decode(parameterString, "utf-8");
}else{
parameterString = URLDecoder.decode(parameterString, "gbk");
}
} catch (UnsupportedEncodingException e) {
logger.info("url解码出错", e);
}
String[] parameters = parameterString.split("&");
for(String str : parameters){
if(str.indexOf("query=")!=-1){
query = str.substring(str.indexOf("=")+1);
}
}
}
其中对应的CharTools中的方法为:
public boolean isUtf8Url(String text) {
text = text.toLowerCase();
int p = text.indexOf("%");
if (p != -1 && text.length() - p > 9) {
text = text.substring(p, p + 9);
}
return Utf8codeCheck(text);
}
private boolean Utf8codeCheck(String text) {
String sign = "";
if (text.startsWith("%e"))
for (int i = 0, p = 0; p != -1; i++) {
p = text.indexOf("%", p);
if (p != -1)
p++;
sign += p;
}
return sign.equals("147-1");
}
这样就可以将不同的编码传过来的url,进行不同的处置。
接下来,这时候本以为完事大吉,没想又出了新的问题,即:在ie下汉字传值还是乱码,我通过后台检测发现,ie的request.getQueryString(),获取的是“query=????”,汉字没有以编码后的形式传送过来,然后再检查request.getParameter("query"),发现是正常的,这时候就可以通过对发送到后台的客户端就行判断,如下:
String agent = request.getHeader("User-Agent");
if(agent.indexOf("MSIE")!=-1){//ie情况比较特殊,不过此时在parameters中是正常的
query = request.getParameter("query");
}
这样在ie下拿到的汉字也就可以正常的显示了。
解决方案三:觉得最好的解决方案还是写个filter,现在项目比较紧张,以后要写,这样就可以忽略掉具体的请求,从整体层次进行解决问题。大致思路就是将方案二的解决办法浓缩到filter中。以后再写
还有一些感想,觉得chrome也不是那么完美,汉字传值不自动转码,给开发造成不少麻烦,还是firefox比较强大~