HTTP 协议的编码问题

用Get方式传中文参数存在很多问题!一不小心就会造成服务器收到的是乱码!所以一般情况下,都是尽量使用Post方法实现中文参数的传参。但是有的时候会出现意料之外的情况不得不用Get方法。解决方法有很多:

  1. 使用Javascript(encodeURI/encodeURIComponent函数)对URI或参数进行UTF-8编码,然后在服务器端解码
  2. 不使用Get头部存放参数,而是用实体部分存放参数(对于Java servlet来说可以,其他情况不知道。因为一般情况Get方法的Http通讯只有头部没有实体。所以此方法有局限性,Javascript实现不了)
  3. 使用Post(与之前说的一样,有特殊情况用不了Post,如用js直接打开一个url并下载其内容)

接下来详细解释下其中原委!

为什么中文会乱码?

这要从Http协议开始说起。Http通讯协议分为头部、实体两个部分。实体部分是可选的,如Get方法就不一定需要。当返回Get的请求时,实体部分的编码是根据头部中的content的值决定的。例如:Content-Type:text/html; charset=UTF-8这就设定了实体的编码为utf-8格式。

  • Post方式的请求参数是放在实体部分中的;
  • 使用Javascript进行Post传参时,实体部分的编码是根据所在文档的编码进行编码;
  • Get方式的请求参数是放在头部的ur中;
  • 使用Javascript进行Get传参时,头部的编码只支持ASCII字符集。所以浏览器会对URI进行编码。

对于get方法来说,都是把数据串联在请求的url后面作为参数,url拼接完成后,浏览器会对url进行URI encode,然后发送给服务器。URI encode的过程就是把部分的url做为字符,按照某种编码方式(如:utf-8,gbk等,各浏览器不同)编码成二进制的字节码,然后每个字节用一个包含3个字符的字符串 “%xy” 表示,其中xy为该字节的两位十六进制表示形式。另外也会将空格替换成”+”。详细过程可以参看JDK源码中的URIEncode类的实现。

可以看到“各浏览器的编码不同”且用户可以自己设置默认编码,这导致了很多不同可能。这也就是为什么IE可以firefox乱码,这个机器可以另一台机器乱码的根源。你无法确定不同的浏览器是使用了什么编码对URI中的非ASCII字符进行编码(ascII可读字符不编码)的,所以你无法在服务器是确定自己使用什么解码。更要命的是不同的服务器也有自己默认的解码。例如Tomcat的解码格式为ISO-8859-1(可以修改server.xml修改)。

如何实现更通用的解决方案?

在开篇时一共提到了三个解决方案,第二三种因为将参数放到了实体部分所以很安全放心,这两种编码你可以通过程序方便的控制。但它们都有局限性不能作为通用方案。所以第一种方案最可行。

具体的步骤:

  1. 浏览器端使用encodeURI对URI进行编码两次
  2. 服务器端使用URI decode(UTF-8)解码一次(Java:URIDecode.decode(“参数”,”utf-8″);)

首先了解下encodeURI与encodeURIComponent函数

encodeURI与encodeURIComponent都是将字符串进行URI encode(都使用utf-8编码),过程之前已经提到过了,但是有所区别。encodeURI有以下字符不会被编码:“!@#$&*()=:/;?+”,另外encodeURIComponent方法有以下字符不会被编码:“!*()”。浏览器的默认URI encode则是所有ASCII字符不会被编码。

为什么编码两次,解码一次呢?

首先浏览器只会对非ASCII字符进行编码,所以在经过两次或一次encodeURI编码之后,浏览器的编码不会起作用。那为什么要进行两次编码呢?

因为不确定服务器端是使用何种编码进行URI的解码。当然如果你很确信你使用的平台是固定的那就不需要了。如果想要代码跨平台则需要考虑。光这么将不够直接,看下的过程(假设服务器为Tomcat):

“中文”  ==encodeURI==>  ”%E4%B8%AD%E6%96%87″  ==encodeURI(%被编码)==>  ”%25E4%25B8%25AD%25E6%2596%2587″   ==Tomcat解码(ISO-8859-1)==>    ”%E4%B8%AD%E6%96%87″ ==Java decode(UTF-8)==>  ”中文”

可以看到进行了两次utf-8编码,一次ISO-8859-1解码,一次utf-8解码。因为ISO-8859-1与utf-8都包含了ASCII字符集(%属于其中之一),所以不会出现乱码。

jQuery中的陷阱

在使用jQuery中进行ajax操作时,我们只在浏览器端编码一次,然后在服务器端手动解码一次即可,为什么呢?

因为jQuery在ajax操作时,默认进行了一次编码:

function add( key, value ){
    s[ s.length ] = encodeURIComponent(key) + '=' + encodeURIComponent(value);
};

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章