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);
};

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