使用 Ajax,可以從非同步物件的 responseText
取得伺服端的回應文字,不過要注意,伺服端回應時若沒有指明編碼(例如 Content-Type: text/html; charset=Big5
之類),responseText
預設會使用 UTF-8 編碼來解讀傳回的文字。如果回應是 XML,則可以使用 responseXML
取得剖析後的 DOM 物件。
在〈URL 編碼〉中介紹過如何為請求參數進行編碼,如果是透過表單發送請求,瀏覽器會自動進行 URL 編碼,如果是透過 Ajax,以非同步物件發送請求參數,有的瀏覽器會進行 URL 編碼,有的瀏覽器不會,為了跨瀏覽器,最保險的方式,就是自行處理 URL 編碼,再透過非同步物件發送請求。
在 JavaScript 中,可以使用 encodeURIComponent()
編碼,編碼後的結果是遵守 RFC 3986 的規範,但正如〈URL 編碼〉中介紹過的,在 RFC 3986 之前,HTTP 亦規範了 GET 與 POST 在發送請求參數時的編碼,大致上也是編碼為 %hexhex,不過空白字元是編碼為 +
而不是 RFC 3986 的 %20
。
如果直接透過瀏覽器按下發送按鈕來送出表單,瀏覽器會自動處理編碼(依網頁上指定的編碼來處理),並將空白字元編碼為 +
,但透過非同步物件發送請求參數時,必須自行處理。
發送請求參數時,若使用 encodeURIComponent()
編碼後,要再將 %20
取代為 +
,以符合 HTTP 的規範。
要注意的是,在字串處理方面,JavaScript 支援 Unicode,內部實作上採用 16 位元編碼每個字串元素,大致上可視為 UCS-2/UTF-16(這當中還有些歷史因素造成的細節,詳見《Effective JavaScript》條款七),不過,傳入 encodeURIComponent()
的字串最後會以 UTF-8 進行編碼,若將 encodeURIComponent()
的結果透過非同步物件發送出去,伺服端必須以 UTF-8 來處理接收到的字串。
下面這個範例是 GET 的一個示範,在新增書籤時,若 URL 已重複(已有的書籤是 https://caterpillar.onlyfun.net 與 https://openhome.cc)則以訊息提示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script type="text/javascript">
window.onload = function() {
var xhr = window.XMLHttpRequest &&
(window.location.protocol !== 'file:'
|| !window.ActiveXObject) ?
function() {
return new XMLHttpRequest();
} :
function() {
try {
return new ActiveXObject('Microsoft.XMLHTTP');
} catch(e) {
throw new Error('XMLHttpRequest not supported');
}
};
function param(obj) {
var pairs = [];
for(var name in obj) {
var pair = encodeURIComponent(name) + '=' +
encodeURIComponent(obj[name]);
pairs.push(pair.replace('/%20/g', '+'));
}
return pairs.join('&');
}
document.getElementById('url').onblur = function() {
var request = xhr();
request.onreadystatechange = function() {
if(request.readyState === 4) {
if(request.status === 200) {
var message = '';
if(request.responseText === 'urlExisted') {
message = 'URL 已存在';
}
document.getElementById('message')
.innerHTML = message;
}
}
};
var params = param(
{ url : document.getElementById('url').value}
);
request.open('GET', 'Ajax-1.php?' + params +
'&time=' + new Date().getTime()); // 避免GET被快取
request.send(null);
};
};
</script>
</head>
<body>
新增書籤:<br>
網址:<input id="url" type="text">
<span id="message" style="color:red"></span><br>
名稱:<input type="text">
</body>
</html>
如果要發送 POST,記得要使用 setRequestHeader()
設定內容類型,因為 POST 要發送的資料會放在請求的本體中,必須告知發送的資料類型為何,例如發送表單類型資料時,必須設置請求標頭 'Content-Type'
為 'application/x-www-form-urlencoded'
,如果是 XML,設定請求標頭的 'Content-Type'
為 'text/xml'
,如果是 JSON,請求標頭 'Content-Typ'
建議設為 'application/json'
。以下是個示範:
...
var url = 'somewhere';
var queryString = 'a=10&b=20';
xmlHttp.open('POST', url);
xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xmlHttp.send(queryString);
既然提到了 encodeURIComponent()
,這邊順便比較一下與 encodeURI()
及 escape()
的差別。
正如先前所言,encodeURIComponent()
可使用在編碼請求參數,它不編碼的字元有英文字母、數字、-
、_
、.
、!
、~
、*
、'
與 、
。
encodeURI()
與 encodeURIComponent()
類似,傳入的字串會以 UTF-8 處理,不編碼的字元除了 encodeURIComponent()
不編碼的字元之外,對;
、,
、/
、:
、@
、&
、=
、+
、$
也不加以編碼,所以像 URL 重新導向之類的需求,必須使用 encodeURI()
,例如:
location.href=encodeURI("https://openhome.cc");
escape()
事實上是一個不建議再被使用的函式,它對 0 到 255 以外的字元,編碼為 %u 型式,而不是 URL 編碼,不編碼的字元除了 encodeURIComponent()
不編碼的字元之外,對 @
、*
、/
、+
也不加以編碼。