由 XMLHttpRequest 送出中文資料給 Tomcat
The following examples had been tested on Mozilla's Firefox and Microsoft's
IE. The document is provided as is. You are welcomed to use it for
non-commercial purpose.
Written by: 國立中興大學資管系呂瑞麟
請勿轉貼
看其他教材
在嘗試 Dojo 和 Yahoo! User Interface Library (YUI) 的時候,才突然發現之前的
AJAX 範例都沒有從 XMLHttpRequest 傳送中文的資料給遠端的 Tomcat servlet。
而發現的原因在於為 Dojo 和 YUI 所寫的程式中,其中一個範例是由 browser 送出
一個中文的(Big5)欄位資料到遠端的 Tomcat servlet 處理,而處理的結果
卻是亂碼。這樣子一來就打亂了我的休假,連續兩個晚上測到凌晨一、兩點
(對一個老人來說,是很大的折磨)。這兩天找了不少資料,目前以
的文章比較有參考價值。尤其,OC'onner 的文章直接說明目前的傳送以及 encoding
的方式並沒有標準,完全要看你所使用的工具而定。看了這個說明之後,測試的方向
就比較明確一些。大概需要檢查的細節有:
- 由 XMLHttpRequest 送出去的 URL:如果 URL 包含 Big5 的資料,XMLHttpRequest
會不會 encode,而且又是以何種方式 encode?
- server 是如何解讀收到的 URL:目前知道 Tomcat (我的主要 servlet 開發
平台)是採用 ISO-8859-1 編碼方式來接收,因此解碼的方式可以用 ISO-8859-1
來進行。但是,如果 browser 送過來的資料是以 UTF-8 編碼,那要怎麼辦呢?
目前,已經找出 Microsoft IE 6.x/7.x 以及 Firefox 1.5.x 或者新版本的
解決方案了。測試出來的情形整理如下:
- IE 6.x/7.x:
- 若 Javascript 對中文資料 encodeURIComponent(),則 servlet 需要對接收到的資料先以 ISO-8859-1 解出來之後,以 UTF-8 編碼出來。
- 若 Javascript 不對中文資料進行 encodeURIComponent(),則 servlet
需要對接收到的資料先以 ISO-8859-1 解出來之後,以 Big5 編碼出來。
- Firefox 1.5.x 或者新版本:不論 browser 是否對中文資料 encodeURIComponent(),servlet 需要對接收到的資料 先以 ISO-8859-1 解出來之後,以 UTF-8 編碼出來。
從剛剛的討論結果可以知道:如果希望寫一個程式能夠同時使用在 IE 和 Firefox
的環境下正確的執行,在 XMLHttpRequest 送出要求之前,最好先對中文
的資料進行編碼(也就是使用 encodeURIComponent 對傳送的資料先
進行編碼),而送到 Tomcat 之後, servlet 必須先以 ISO-8859-1 的方式接收之後,
再依據 UTF-8 的方式解碼使資料回到中文的格式,你可以
連結 測試網頁
試試看。
有了以上的認知,AJAX 網頁的程式部份,只有小部份需要修改。以測試網頁
為例,因為會被傳送的資料只有一個,也就是名為 elements[0] 的文字欄位,
所以我們只需要再傳送出 url 之前,將該部分的資料利用 encodeURIComponent
進行編碼;程式碼如下所示,其中需要編碼的部份以綠色字體標示出來。
請注意這樣的設計方式,也可以處理英文,所以不必為了英文,另外又寫一段程式。
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Big5">
<script type="text/javascript" language="javascript">
function makeRequest(url) {
var http_request = false;
if (window.XMLHttpRequest) { // Mozilla, Safari,...
http_request = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE
try {
http_request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
http_request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {}
}
}
if (!http_request) {
alert('Giving up :( Cannot create an XMLHTTP instance');
return false;
}
// 定義事件處理函數為 alterContents()
http_request.onreadystatechange = function() {
alertContents(http_request); };
// IE 6.x 和 Firefox 1.5.x 皆要 encodeURI()
url = url + "?name=" +
encodeURIComponent(document.myform.elements[0].value);
http_request.open('GET', url, true);
http_request.send(null);
}
function alertContents(http_request) {
if (http_request.readyState == 4) {
if (http_request.status == 200) {
var mesg = http_request.responseText;
alert(mesg);
} else {
alert('There was a problem with the request.');
}
}
}
</script>
</head>
<body>
<form name="myform">
姓名: <input type="text" name="name"></input>
<input type="button" value="OK" onClick="makeRequest('適當的 servlet URL');"></input>
</form>
</body>
</html>
遠端的 servlet 程式碼如下所示。由於傳送的參數名稱為 name,而收到 name 的值
由於被 Tomcat 當作 ISO-8859-1 的資料處理,所以我們需要先以 ISO-8859-1
的方式解讀該值,並形成位元組陣列;由於之前中文的資料以 encodeURIComponent()
編碼過(也就是 UTF-8 的資料),所以在下列程式的綠色字體部份,我們再以
UTF-8 的方式將其還原回來。
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class Hello extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
res.setContentType("text/plain;charset=UTF-8");
PrintWriter output = res.getWriter();
String msg = req.getParameter("name");
msg = new String(msg.getBytes("ISO-8859-1"), "UTF-8");
StringBuffer buf = new StringBuffer();
buf.append("Hello " + msg);
buf.append(" , 歡迎使用 AJAX");
output.println(buf.toString());
output.close();
}
}
為了讓讀者更清楚了解編碼和解碼的過程,整個過程以下圖呈現:
瀏覽器中的中文 --經過 encodeURIComponent() 轉碼(這部份的程式碼是為了執行跟所有瀏覽器預設的動作)
--經過 Tomcat 編碼 --> ISO-8859-1 (Tomcat 無法知道客戶端送過來的編碼為何,因此它會先 decodeURI() 然後把資料以 ISO-8859-1 編碼)
--經過 servlet 的 getBytes("ISO-8859-1") 以 ISO8859-1 的方式解讀 URL 的資料
--再經過 new String(..., "需要的編碼") 將資料從 ISO-8859-1 的編碼轉換成需要的編碼;在這個範例中,由於程式碼中的中文是以
UTF-8 編碼(我在最近一次升級伺服器之後,將系統預設編碼設定成 UTF-8),所以回傳的資料編碼為 UTF-8(程式碼中的
setContentType() 可以看得出來)。
個人心得: 雖然非常多的專家都認為應該將 Linux 的預設編碼設定為
UTF-8 (也就是 zh_TW.UTF-8),但是經過一段時間的使用之後,我還真的
有點後悔,原因很簡單:我的客戶端作業系統是 XP Professional 繁體中文版,
而且之前開發的一些程式(內含中文)都是以 XP 的預設編碼來做的,也就是
大五碼(Big5),在這樣的環境下,不斷的注意編碼的一致性實在很痛苦,所以
我個人建議還是將 Linux 的預設編碼設定為 Big5 (也就是 zh_TW.Big5)
會比較方便。
Last Updated: Friday, 06-May-2011 18:31:06 CST
Written by: 國立中興大學資管系呂瑞麟