HTTP定義GET來取得所需的資源,GET用於等冪(idempotent)操作,也就是多次GET操作,都必須傳回相同的結果,GET請求顧名思義,原則上不影響伺服端的狀態。
如果使用傳統表單發送GET請求,GET的請求參數會出現在網址列並更新頁面,但使用非同步物件時,GET的請求參數並不會影響網址列,所以無法讓使用者以請求參數作為書籤之用。
要使用非同步物件透過GET發送請求參數,只要在第二個url參數中以請求參數格式附加,而send()時不傳入引數設為null即可。一個例子如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN">
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<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');
}
};
document.getElementById('category').onchange = function() {
var request = xhr();
request.onreadystatechange = function() {
if(request.readyState === 4) {
if(request.status === 200) {
document.getElementById('book').innerHTML =
request.responseText;
}
}
};
request.open('GET', 'GET-1.php?category=' + this.value +
'&time=' + new Date().getTime()); // 避免GET被快取
request.send(null);
};
};
</script>
</head>
<body>
圖書:<br>
<select id="category">
<option>-- 選擇分類 --</option>
<option value="theory">理論基礎</option>
<option value="language">程式語言</option>
<option value="web">網頁技術</option>
</select><br><br>
採購:<div id="book"></div>
</body>
</html>
這個例子是連動選單,下一個下拉選單的選項是根據上一個下拉選單的選擇而定,事先不在網頁中寫死第二個選單的選項,而是根據上一個選單所發送的請求參數而定,例如若請求參數為category=theory,則會傳回以下的HTML片段:
<select>
<option value="algorithm">常見演算</option>
<option value="graphic">電腦圖學</option>
<option value="pattern">設計模式</option>
</select>
<option value="algorithm">常見演算</option>
<option value="graphic">電腦圖學</option>
<option value="pattern">設計模式</option>
</select>
當然,直接傳回HTML片段,並不是很好的方式,因為伺服端綁死了客戶端的頁面設計。這個範例只是用來示範GET的請求發送,之後會看到若傳回XML或JSON等其它資料格式,客戶端將有彈性自行決定頁面設計方式。
另外要注意的是,GET請求時若URL相同,瀏覽器可能會作快取(尤其是Internet Explorer),為了避免取得舊的資料,你可以在URL上附加時間戳記,讓每次URL不同,以避免瀏覽器作快取的動作。
Web的世界中,故事往往不會這樣就結束。GET在發送請求時,必須注意編碼的問題,因為/、?、@、空白等字元,在URL中是保留字,RFC 3986 規範了哪些字作為保留字,如果你要在URL表達這些保留字或一些非ASCII字元,必須使用%hexhex編碼形式。例如http://caterpillar.onlyfun.net/add.php?url=https://openhome.cc,若要在URL中表示,必須如此處理:
http://caterpillar.onlyfun.net/add.php?http%3A%2F%2Fopenhome.cc
其中%3A%2F%2F分別就是://三個字元編碼處理後的結果。
在JavaScript中,可以使用encodeURIComponent()為你作這些字元的編碼,編碼後的結果是遵守RFC 3986的規範,然而在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來處理接收到的字串。
如果你沒有透過encodeURIComponent()編碼,就直接透過非同步物件送出非ASCII字元,例如中文,那非同步物件會如何處理編碼,依不同的瀏覽器而有所不同,因此,建議還是使用encodeURIComponent()編碼後將%20取代為+再送出,避免不同瀏覽器的問題。
下面這個範例是GET的另一個示範,在新增書籤時,若URL已重複(已有的書籤是http://caterpillar.onlyfun.net與https://openhome.cc)則以訊息提示:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN">
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<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', 'GET-2.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>
若URL已存在,則傳回'urlExisted'的字串,此時將訊息設定為「URL 已存在」。