问题在现有主站中使用的是是GBK编码的,当表单使用formsubmit方式递交的话没有问题,服务器端能够正确识别字符编码。但是,当客户端使用ajax的方式递交表单的话,服务器端识别客户端递交的表单的内容,当内容中有中文字符的话就会出现乱码现象。原因分析
问题
在现有主站中使用的是是GBK编码的,当表单使用form submit方式递交的话没有问题,服务器端能够正确识别字符编码。但是,当客户端使用ajax的方式递交表单的话,服务器端识别客户端递交的表单的内容,当内容中有中文字符的话就会出现乱码现象。
原因分析
究其原因,其实很简单,使用ajax方式提交的表单是用utf-8编码来提交的,这样服务器端在接收客户端表单的内容的时候还是按照GBK或其他编码方式来解析的,自然解析出来的内容会出现乱码了。
表单内容可以通过GET或POST方式发送,但是用这两种方式有几个不同之处,对“你好”调用javascript函数encodeURI()解析之后得到的字符串为“%E4%BD%A0%E5%A5%BD”,需要注意的一点,encodeURI()这个函数只会对字符串进行utf8编码。当客户端把“%E4%BD%A0%E5%A5%BD”这段字符串通过GET或者POST方式发送会发生什么事。
1.GET方式
当用get方式提交表单,在服务端的tomcat服务器中会用一个connector来自动解析编码之后字符串,以下是connector的配置节点:
maxHttpHeaderSize="8192"
maxThreads="150" minSpareThreads="25"
maxSpareThreads="75"
enableLookups="false" redirectPort="8443"
acceptCount="100"
connectionTimeout="20000" disableUploadTimeout="true"
URIEncoding="GBK" />
上面配置中有一个属性URIEncoding="GBK"的作用是,当客户端get发送信息中有encode模式的字符串就是用gbk来decode它。和遗憾,主站服务器中的URIEncoding属性是gbk不能变了,而客户端encodeURI()只能编码UTF-8的字符。所以,用这种方式发送的字符串在服务器端就会出现乱码现象。
2. POST方式
当客户端发送“%E4%BD%A0%E5%A5%BD”字符串时,是通过YAHOO.util.Connect这个控件来发送的,如果客户端能正常地发送“%E4%BD%A0%E5%A5%BD”这个字符串,服务端接收到之后并不会做额外的转义,但是YAHOO.util.Connect 将encode之后字符串传给Connect控件之后,这个控件在内部会将encode之后的字符再decode之后再发送,因为发送的是utf-8格式的内容,所以服务器端就会出现乱码。
解决办法
尝试了以上get和post方式之后,觉得在请求的URL上发送数据乱码问题的解决方案比较麻烦,况且,http协议中已经明确说了get和post的区别,get是从服务器端拿一些东西,而post是向服务器传输东西。get方式向服务器端发送数据的数据量有限,且get传输也是不安全的。
那么我们只能来解决YAHOO.util.Connect这个控件发送请求之前自动转义字符的问题。以下是Connect发送异步请求的一段代码
var postData="city="+from["city"].value
+"&areainfo="+from["areainfo"].value +"&store.name="+encodeURI(encodeURI(from["store.name"].value))
YAHOO.util.Connect.asyncRequest('POST',formObject.action, callback,postData);
如上所示,可以调用两遍encodeURI,
例如:“你好”
·调用一遍encodeURI:“%E4%BD%A0%E5%A5%BD”
·调用两遍encodeURI: “%25E4%25BD%25A0%25E5%25A5%25BD”
当把第二遍encode的结果传给YAHOO.util.Connect控件,控件会decode成第一遍调用encodeURI的输出结果,这样就能把“你好”的utf-8编码传输到服务器上去了。
在服务器的webwork的action或者interceptor中先对传输上来的内容做一次decode,注意一定要用utf-8解码。示例
try {
HttpServletRequest request
= ServletActionContext.getRequest();
String storeName = request.getParameter("store.name");
this.getStore().setName(
URLDecoder.decode(StringUtils.trimToEmpty(storeName)
,"UTF-8"));
this.getStore().setAddress(
URLDecoder.decode(
StringUtils.trimToEmpty(request
.getParameter("store.address")), "UTF-8"));
} catch (Exception e) {
throw new RuntimeException(e);
}
总结
通过以上提到的在客户端中将用ajax递交的字段内容两次调用encodeURI的方法,能够很好地解决服务器端使用非utf8的而造成的乱码问题。
出现这样的问题,主要原因是客户端javascript的编码只支持utf编码,而服务器端能够按照架构师的需要定义各种编码,口碑现在服务器端使用的是GBK的编码格式,但是随着业务扩展,某些应用模块使用了utf的编码格式,这样在服务器端由于编码不统一,在做两个不同编码的格式系统的产品的时候给程序员带来了无尽的烦恼。例如,sa同时经常会问这样的问题,调用数据库的应用有用gbk编码的,也有用utf8编码的,那么数据库应该用啥编码的呢?
最近口碑的工程师们为现在一直为到底是用gbk编码还是用UTF8编码在进行讨论,我们现在讨论的焦点似乎是哪个编码更加好。但是我觉得,就编码来说扩展性当然是utf8,如果以后口碑要打入国际市场,到时候要出一个阿拉伯语版本的应用时候就可以轻松搞定了。我倒是觉得讨论那个编码好没有什么实际意义,关键是如何解决现在编码混乱的局面,如何让编码统一,这个才是我们应该关注的焦点。这样在将来的项目中我们的工程师不再会因为编码问题而头痛了。
所以我的个人观点是,当你开始搭建一个新的系统的时候,一开始就把utf-8作为你的项目中使用的编码格式,并且要把这个成为你项目的规约,写进你们项目组的“宪法”中去。这样,在以后会为你免去很多不必要的麻烦。