这两天写一个项目遇到一个很怪异的现象,就是在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,进行不同的处置
next
这时候本以为完事大吉,没想又出了新的问题,即:在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比较强大~