ajax中文亂碼問題的總結

來源:互聯網
上載者:User

標籤:blog   http   java   使用   os   檔案   

ajax中文亂碼問題的總結 2010-12-11 22:00 5268人閱讀 評論(1) 收藏 舉報 ajaxurljavascriptservletcallback伺服器

本章解決在AJAX中常見的中文問題,分析中文亂碼產生的原因,以及如何解決亂碼問題

1. HTTP協議的編碼規定

在HTTP協議中,瀏覽器不能向伺服器直接傳遞某些特殊字元,必須是這些字元進行URL編碼後再進行傳送。url編碼遵循的規則:

 

將空格轉換為(+)

 

對0-9,a-z,A-Z之間的字元保持不變。

 

對於所有其他的字元,用這個字元的當前字元集編碼在記憶體中的十六進位格式表示,並在每個十六進位位元組前加上一個百分比符號%。例如,字元“+”用%2B表示,字元“=”用%3D表示,字元“&”用%26表示,字元“國”用%B9%FA表示注意,同一個中文字元在不同的字元集編碼方式下,在記憶體中的編碼值也是不同的,一個字元的URL編碼是針對字元在記憶體中的碼值而言的,採用不同編碼的同一個字元的URL編碼結果是不同的。

 

2. encodeURI()與encodeURIComponent()函數

javaScript中提供了兩個函數來對字元進行URL編碼:encodeURI()與encodeURIComponent(),兩者的區別在於,encodeURI函數不會對以下的字元進行處理: “! @ # $ & * ( ) = : / ; ? + ‘ ”,而encodeURIComponent函數會對更多的字元進行處理比如 URI的組成部分 “/” 就會被encodeURIComponent進行處理。這兩個方法對傳遞的值進行URL編碼,過程是先找到字元所對應的UTF-8編碼,比如“張三”兩個字的UTF-8編碼是”0xE5BCA0E4B889”(前面的是零x,表示是16進位編碼).“張”是”0xE5BCA0”,”三”是”0xE4B889”,那麼被轉換後的結果就

是”%E5%BC%A0%E4%B8%89”,注意這個轉換結果與網頁的編碼沒有任何關係,因為這兩個函數總是拿到字元所對應的UTF-8碼,然後再進行URL編碼的。也就是說不管網頁是GBK的編碼還是UTF-8的編碼,轉換的結果都一樣。

所以如果我們發送給伺服器的請求包含有中文或者其它比較特殊的字元如空格“+”等符號的時候,就就需要使用者兩個函數對字元進行URL編碼。

3. 封裝Ajax請求代碼,供後面使用。

建立一個web項目,在web項目中添加一個ajax.js檔案,內容包含兩個函數如下:

createXmlHttp()

function createXmlHttp() {

if (window.XMLHttpRequest) {

//alert("非IE瀏覽器");

return new XMLHttpRequest();

} else if (window.ActiveXObject && !window.XMLHttpRequest){

var aVersion = ["MSXML2.XMLHttp.6.0",

"MSXML2.XMLHttp.5.0", "MSXML2.XMLHttp.4.0",

"MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp",

"Microsoft.XMLHttp"];

for (var i = 0; i < aVersion.length; i++) {

try {

var oXmlHttp = new ActiveXObject(aVersion[i]);

//alert("IE瀏覽器版本"+aVersion[i]);

return oXmlHttp;

}

catch (ex) {}

}

}

throw new Error("建立XMLHttpRequest對象出錯!");

}

doGet(url,callBack)函數,該函數有兩個參數,將來要發送AjAX GET請求可以直接調用該方法。 第一個參數表示要發送的請求的URL地址,第二個是回呼函數,回呼函數需要處理從服務端返回的資料。

/**

* @param url 請求的URL地址

* @param callBack 回呼函數

* @return

*/

function doGet(url,callBack){

var request=createXmlHttp();

request.onreadystatechange=function(){

if(request.readyState==4 && request.status==200){

//注意我們定義回呼函數的時候要多加一個參數接收返回的資料

callBack(request.responseText);

}

};

request.open("GET",url);

request.send(null);

}

4. 編寫頁面,該頁面使用的字元集是UTF-8編碼:

HTML部分:

<body>

<h3>驗證使用者名稱是否存在</h3>

輸入使用者名稱:<input type="text" id="userName" /> <span id="warning"></span><br />

<input type="button" value="驗證" onclick="checkUserName(‘userName‘)" />

</body>

JavaScript部分:

首先引入ajax.js檔案,然後編寫當按鈕點擊的時候的要執行的代碼:

<script type="text/javascript" src="ajax.js"></script>

<script type="text/javascript">

function checkUserName(tagID){

//擷取文字框中輸入的值

var userName=document.getElementById(tagID).value;

//對中文進行URL編碼

①var url="ajax.do?"+encodeURI("userName="+userName);

//data是從服務端返回來的資料

doGet(url,function(data){

document.getElementById("warning").innerHTML=data;

});

}

</script>

頁面效果:

 

當在文字框中輸入“張三”後,點擊驗證後,javaScript代碼執行到 ① 之後,url的值就變成了 “ajax.do?userName=%E5%BC%A0%E4%B8%89”,可以通過firefox瀏覽器的firebug外掛程式進行斷點調試,得到發送的url的值。

這裡為什麼沒有使用encodeURIComponent()函數呢?這是因為encodeURIComponent函數會將”=”變成“%3D”,“?”變成” %3F”, 如果有多個參數的話會用到“&”符號,同樣也會被轉換,而這些字元不用轉換也可以提交,所以這裡使用了encodeURI,這個函數不會對”?”,”=”,”&”進行轉換。後面的“%E5%BC%A0%E4%B8%89”就是“張三”兩個漢字按照UTF-8字元集進行URL編碼之後的結果

5. 在服務端取得發送過來的資料

編寫一個Servlet,這個Servlet的映射是 /ajax.do,其中的doGet方法如下:

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

//告訴用戶端響應的資訊的編碼格式是UTF-8

response.setContentType("text/html;charset=UTF-8");

②String userName=request.getParameter("userName");

PrintWriter out=response.getWriter();

out.print("您要驗證的使用者名稱是:"+userName+",該使用者名稱可以使用");

}

我們在②處放置一個斷點,然後以斷點的方式啟動Tomcat,提交後程式進入斷點處我們發現取得的userName的值是: “??????”,為什麼會是亂碼?

我們分析一下,用戶端Ajax想伺服器發送的請求是

”ajax.do?userName=%E5%BC%A0%E4%B8%89”,那麼伺服器上的

request.getParameter()方法在取參數值的時候,首先要進行URL解碼(其實就是去掉字元當中的“%“),解碼之後將只剩下的位元組部分按照Tomcat在內部預設的ISO-8859-1字元集的方式轉換成字串,於是亂碼開始在這裡出現 了。因為發送過來的位元組在去掉%後剩下的位元組應該按照UTF-8轉換字串才對,但是卻採用了ISO-8859-1,於是亂碼產生了。

那麼知道原因之後,解決起來就很容易了。既然是按照ISO-8859-1轉換得到的字串,那我們就得到這個字串還原為ISO-8859-1的位元組,然後再將位元組按照正確的UTF-8轉換為字串,這樣就得到了正確的字元了,修改Servlet中的代碼如下:

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

//告訴用戶端響應的資訊的編碼格式是UTF-8

response.setContentType("text/html;charset=UTF-8");

System.out.println("進入Servlet");

String userName=request.getParameter("userName");

userName=new String(userName.getBytes("iso-8859-1"),"UTF-8");

System.out.println(userName);

PrintWriter out=response.getWriter();

out.print("您要驗證的使用者名稱是:"+userName+",該使用者名稱可以使用");

}

用戶端響應為:

6. 試一試將提交方式改成POST方式

在ajax.js檔案中添加一個函數,該函數專門用於提交POST請求

/**

*

* @param url 要提交的URL

* @param submitData 要提交的資料

* @param callBack 回呼函數

* @return

*/

function doPost(url,submitData,callBack){

var request=createXmlHttp();

request.onreadystatechange=function(){

if(request.readyState==4 && request.status==200){

//注意我們定義回呼函數的時候要多加一個參數接收返回的資料

callBack(request.responseText);

}

};

request.setRequestHeader("Content-Type","application/x-www-form-urlencoded");

request.open("POST",url);

request.send(submitData);

}

修改頁面上的javaScript代碼:

<script type="text/javascript" src="ajax.js"></script>

<script type="text/javascript">

function checkUserName(tagID){

//擷取文字框中輸入的值

var userName=document.getElementById(tagID).value;

//data是從服務端返回來的資料

doPost("ajax.do","userName="+userName,function(data){

document.getElementById("warning").innerHTML=data;

});

}

</script>

當我們發送post請求的時候,儘管我們為要求標頭設定了

application/x-www-form-urlencoded,但是發送的資料並沒有進行URL編碼,而傳統的將form表單的提交方式設定成post,在提交的時候會自動進行URL編碼。

所以Ajax中的post請求時將資料原封不動的傳遞到了伺服器上,所以只需要調用reqeust.setCharacterEncoding() 設定正確的編碼集後,就可以取出資料了。

7. 最佳解決方案

前面的方式我們雖然分別解決了GET方式和POST方式的中文問題,但是需要分開進行處理,並且對於不同的伺服器,預設的編碼集是不同的,這樣對於GET方式我們進行的手工轉碼就不能通用了。

那麼不管是Get請求還是POST,有沒有可以統一的解決方案?我們可以做如下的處理:

 

將提交的資料使用javaScript的encodeURI()進行兩次URL編碼

 

服務端進行一次URL 解碼即可

 

這種方式的優點是與用戶端網頁的編碼集無關,與伺服器的預設編碼集無關,而且能夠相容幾乎所有的瀏覽器。

下面以GET方式為例來理解分析全過程:

修改javaScript代碼為:

<script type="text/javascript" src="ajax.js"></script>

<script type="text/javascript">

function checkUserName(tagID){

//擷取文字框中輸入的值

var userName=document.getElementById(tagID).value;

//data是從服務端返回來的資料

var url="ajax.do? userName="+encodeURI(encodeURI(userName));

doGet(,function(data){

document.getElementById("warning").innerHTML=data;

});

}

</script>

Servlet代碼修改為:

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

//告訴用戶端響應的資訊的編碼格式是UTF-8

response.setContentType("text/html;charset=UTF-8");

String userName=request.getParameter("userName");

userName=URLDecoder.decode(userName,"UTF-8");

System.out.println(userName);

PrintWriter out=response.getWriter();

out.print("您要驗證的使用者名稱是:"+userName+",該使用者名稱可以使用");

}

運行後,在各種瀏覽器中都沒有出現亂碼問題。換成POST方式,也沒有出現亂碼問題。頁面如果換成GBK編碼,也沒有出現亂碼問題.

為什麼這種方式沒有出現問題,為什麼要進行兩次 encodeURI?我們只需要跟蹤一下提交的資料即可:

假如我們提交的是 “張三”:

①我們第一次進行encodeURI之後的結果為:

%E6%9D%8E%E5%9B%9B

②第二次進行encodeURI之後的結果為:

%25E6%259D%258E%25E5%259B%259B

③我們對比一下兩個值,發現第一次URL編碼後中間有%,而第二次URL編碼後將第一次編碼結果中的%替換成了%25,所以最終發送的資料為:

ajax.do?userName=%25E6%259D%258E%25E5%259B%259B

④在服務端的Servlet中,我們通過調用request.getParameter(“userName”)取值的時候,getParameter方法會對%25E6%259D%258E%25E5%259B%259B進行URL解碼,解碼後的結果為%E6%9D%8E%E5%9B%9B,也就是將%25換成了%,那麼此時Tomcat伺服器按照預設的iso-8859-1轉換的字串的時候根本就沒有做任何變換,還是%E6%9D%8E%E5%9B%9B

⑤當我們再次進行URL解碼的時候即: URLDecoder.decode(userName,"UTF-8"),此時去掉其中的%後變成了E69D8EE59B9B,這正好是”張三”的UTF-8編碼,所以使用UTF-8碼轉換成字串“張三“.

從整個過程看來,這種方式的優勢在於與頁面的編碼無關,也與伺服器所使用的編碼集無關。我們需要做的只需要將提交的資料(不管是POST的資料還是GET的資料),進行兩次encodeURI即可。 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.