一次乱码经历的思考

直接正题

品牌专区中遇到的乱码问题:

雅诗兰黛品专物料文字中包含特殊字符Estée Lauder正式发布后自动转换成了(Est Lauder)  ,以至于线上广告中出现乱码。

乱码分析:

     现象: Web前端à escape编码处理à ajax提交 à后台解码保存Estée Lauder

<!--[if !supportLists]-->1.  <!--[endif]-->提交前为什么要用escape处理?

escape进行处理,是因为我们的server端是gbk编码的(struts和过滤器中都有配置,实际上还没到struts,在过滤器中就已经按gbk处理request参数了)。

ajax提交时,默认会对参数进行encodeURIComponent编码处理,而encodeURIComponent编码默认是按utf8的(这个无法控制),如果不用escape处理,就相当于我们提交到server后端的编码是utf8编码的,那么过滤器或strutsgbk编码来解码utf8格式的字符,可想而知,一定乱码。

2. 那么escape帮我们做了什么呢?另外,encodeURIComponent这个是干啥的呢,还有一个叫encodeURI,这三个你在web前端js编码时经常会遇到。

让我们一起来认识一下这三个js函数吧。(具体请参见:http://www.cnblogs.com/phpzxh/archive/2011/05/23/2054334.html

扫描二维码关注公众号,回复: 643918 查看本文章

1escape() 方法

MSDN JScript Reference中如是说:

The escape method returns a string value (in Unicode format) that contains the contents of [the argument]. All spaces, punctuation, accented characters, and any other non-ASCII characters are replaced with %xx encoding, where xx is equivalent to the hexadecimal number representing the character. For example, a space is returned as "%20."

译:escape方法以Unicode格式返回一个包含传入参数内容的string类型的值 Escape方法会将传入参数中所有的空格、标点符号、重音字符以及其它任何非ASCII字符替换为%xx的编码形式,其中xx与其所表示的字符的16进 制数表示形式相同。如空格字符的16进制表示形式为0x20,则此时xx应为20,即escape(‘ ’) 返回“%20”。

2encodeURI()方法

MSDN JScript Reference中如是说:

The encodeURI method returns an encoded URI. If you pass the result to decodeURI, the original string is returned. The encodeURI method does not encode the following characters: ":", "/", ";", and "?". Use encodeURIComponent to encode these characters.

译:encodeURI方法返回 一个经过编码的URI。如果将encodeURI方法的编码结果传递给decodeURI方法作参数,则能得到原始的未编码的字符串。需要注意到是 encodeURI方法不编码如下字符":", "/", ";", and "?"。如果想要编码这些字符,请使用encodeURIComponent方法。

3encodeURIComponent()方法

MSDN JScript Reference中如是说:

The encodeURIComponent method returns an encoded URI. If you pass the result to decodeURIComponent, the original string is returned. Because the encodeURIComponent method encodes all characters, be careful if the string represents a path such as /folder1/folder2/default.html. The slash characters will be encoded and will not be valid if sent as a request to a web server. Use the encodeURI method if the string contains more than a single URI component.

译:encodeURIComponent方法返回一个编 码过的URI。如果将encodeURIComponent方法的编码结果传递给 encodeURIComponent方法作参数,则能得到原始的未编码的字符串。因为encodeURIComponent方法会编码所有的字符,所以 如果待编码的字符串是用来表示一个路径(如/dir1/dir2/index.htm)时,就一定要小心使用了。‘/’符号会被其编码之后,将不再是一个 有效的路径标识符,所以不能被web服务器正确地识别。当字符串包含一个单独的URI component(?后面的请求参数)的时候,请使用此方法

3.进一下分析:

正如上面的escape函数介绍(见“飘黄“)所说,escape的原理是把字符串以unicode的方式编码,这样传到后台后,无论用什么形式的编码(GBKUTF8,它俩可都是认识unicode码的哦),都可以正确的解码我们提交到后台的字符串。那为什么不用另外两个函数(encodeURIencodeURIComponent)呢,原因很简单,它们是将字符串编码成utf8,而我们后台是gbk解码的,所以当然不行。

 

escape不是万能的,遇到特殊字符,它也不一定奏效(具体哪些字符不行,上面的介绍中列举了一些,想要了解更多 ,自己sogou一下吧)。

我简单对Estée做了个测试:

Escape(Estée)编码成unicode后是:Est%E9e,拿这个字符用URLDecoder decode(无论gbk还是utf8)一下,你得到一个乱码:Est?e

也就是说escape转义后的Estée到后台是无法正确解码的。

如果用encodeURIencodeURIComponent编码Estée后是:Est%C3%A9e,我们拿这个用URLDecoder 解码一下,是可以正确解码的,但由于我们后端的大环境是GBK的,我们不能这么简单地修改成encodeURIencodeURIComponent,这样会影响其它的中文参数的ajax提交。

 

(这里再顺便提一下,后端的处理其实还和web容器有关,其实用escape处理后不是标准的unicode码,比如“闫芳”这两字,unicode码对应是\u95EB\u82B3,而escape编码之后是%u95EB%u82B3,你用resin的话,是可以自动帮你处理的,如果你用tomcat的话,很不幸,会抛出解码的异常,呵呵,这里你还要感谢一下可爱的resin,默默帮你做了一些事情,tomcat的话,就要额外多一些处理。)

 

那么,我们遇到此类问题,应该怎么处理呢?

下面一块讨论一下可能的解决方式:

<!--[if !supportLists]-->1.       <!--[endif]-->如果我们是要做一个新系统的话,那么很幸运,我们应该优先选择utf8的编码方式,这样为给我们省去不少麻烦,也许你都不需要配置编码,像springstruts这些流行的框架,你不设置的话,它默认就是按utf8来处理的。即使不用utf8来作工程编码,至不应该做到,jsp页面的编码也应该和后台的编码是一致的,要么都是utf8,要么都是gbk,前两天admin里仍然遇到过前后端编码不一致,导致导出的excel乱码的问题。

<!--[if !supportLists]-->2.       <!--[endif]-->é 这种特殊字符,客服如果和客户沟通好,不支持就最好了,这样对于我们现行的系统,我们维持现在escape的方式基本是能够work的。

如果必须支持特殊字符的话,则需要进一步的解决方案,请继续看3.

 

<!--[if !supportLists]-->3.       <!--[endif]-->方案一:特殊字符的场景下,不要用ajax提交数据,用浏览器form的方式提交。

方案二:对于我们现在server端是gbk编码的场景,在struts和其它过滤器处理前处理request的请求参数(如果server端是utf8的话,就没有这个问题了)

方案三:重写jquery,jqueryajax提交时以unicodegbk的方式提交。

 

方案一:应该是最好的解决方案,对于这种上传数据的请求,尤其是可能会含特殊字符的请求,如果页面效果要求不高,最好不用ajax,用浏览器的form提交,浏览器会根据网页编码自动对数据进行编码,所以就不会服务器和客户端编码不一致了。

 

方案二:

struts之前加一个过滤器,根据request.CharacterEncoding,获取请求参数(当然如果系统里有特殊的场景不希望这样做,也可以在过滤器中进行特殊设置)

 

               对于admin-cpc的应用场景的配置,具体如下:(这种方式提交前,就不需要进行escape处理了)

 

<!--[if !supportLists]-->a)       <!--[endif]-->ajax提交参数显示指定:  contentType:'application/x-www-form-urlencoded; charset=UTF8'

firefoxchrome默认就是utf8的,这里显式写一下是因为浏览器兼容性的问题,ie提交时不会加charset

eg:

            $.ajax({       

              url: "/query!test.action",

              type: "post",

              dataType:"text",

              data:postData,

              contentType:'application/x-www-form-urlencoded; charset=UTF8',

              success: function(html){

                  alert(html);

              }

   });

<!--[if !supportLists]-->b)       <!--[endif]-->Web.xml在最前面加一下过滤器:

            <filter>

       <filter-name>testEncoding</filter-name>

      <filter-class>com.sogou.p4p.action.common.EncodingFilter</filter-class>

    </filter>

    <filter-mapping>

       <filter-name>testEncoding</filter-name>

       <url-pattern>/*</url-pattern>

</filter-mapping>

<!--[if !supportLists]-->c)       <!--[endif]-->过滤器EncodingFilter实现

                           if(request.getCharacterEncoding() == null)

            {

                request.setCharacterEncoding("GBK");//默认按gbk处理

            }

            else

            {

                if(request.getCharacterEncoding().equals("UTF-8"))

                {

                    //当调用过request.getParameter后,再setCharacterEncoding设置编码对请求参数就不起作用了,

                    //所以这里提前获取一下任意的一个参数(existing or not)

                    //以防止后面的过滤器,修改CharacterEncoding

                    request.getParameter("anykey");

                }

       }

 

       3)方案三: 修改jquery的代码,让它不一定用utf8提交ajax请求,这个的思路就是覆盖ajax.Param的实现让它不用encodeURIComponent来进行utf8编码,而用escape转码,但这个似乎也解决不了é这种特殊字符的问题,我在本地尝试时,重写的ajax.Param一直没生效,所以谁有兴趣可以研究一下。

 

方案三其实还是解决不掉像Estée这种特殊字符的问题,而且还要重写jquery,所以极力不推荐,但方案一和二应该都是可行的。

 

编码的问题无处不在,最终还是需要我们了解编码的基本知识和简单的理论,这样遇到问题,就可以游刃有余地处理好。

 

最后,附两篇编码相关的文章吧J

 

 其中一篇是峰哥之前在技术周报中发的,很不错:)

<!--[if !supportLists]-->·         <!--[endif]-->深入分析 Java 中的中文编码问题

摘要:编码问题一直困扰着开发人员,尤其在 Java 中更加明显,因为 Java 是跨平台语言,不同平台之间编码之间的切换较多。本文将向你详细介绍 Java 中编码问题出现的根本原因,你将了解到:Java 中经常遇到的几种编码格式的区别;Java 中经常需要编码的场景;出现中文问题的原因分析;在开发 Java web 程序时可能会存在编码的几个地方,一个 HTTP 请求怎么控制编码格式?如何避免出现中文问题?

https://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/

编者按:大家是否还在不断的碰到编码的问题,是否还在不断的被乱码问题所困扰,是否想想知道乱码的真相?请大家仔细看看这篇篇文章吧,肯定能让你对乱码有深入的认识!

 

另外一篇,是讲python的编码问题的,虽然我们组主要的开发技术是java,但原理是相通的,这篇讲得也很浅显易懂,值得一读。

 

http://fsldn.blog.163.com/blog/static/454643201099101233411/

猜你喜欢

转载自ericlixj.iteye.com/blog/1755010