那么什么是XMLHttpRequest組件呢,它又如何工作的呢?
雖然Ajax技術(shù)流行時(shí)間還比較短暫,但是XMLHttpRequest組件的年齡可不小了。微軟早在IE 5版本(1999年3月)瀏覽器中就以ActiveX對(duì)象的形式引入了XMLHttpRequest對(duì)象,由于當(dāng)時(shí)大家并沒(méi)有認(rèn)識(shí)到它的可愛(ài)之處,加之其他瀏覽器不支持該組件,所以最初誕生的幾年里除了微軟在Outlook中使用該組件外基本上沒(méi)有被人重視。
后來(lái)其他瀏覽器制造商發(fā)現(xiàn)這個(gè)“家伙”很有才,也紛紛在各自的瀏覽器內(nèi)增加對(duì)XMLHttpRequest對(duì)象的支持,但是把它作為一個(gè)本地JavaScript對(duì)象來(lái)使用,而不是作為一個(gè)ActiveX外部組件實(shí)現(xiàn)。如今微軟已在IE 7版本中把XMLHttpRequest升級(jí)為一個(gè)窗口對(duì)象的屬性,可謂連升三級(jí),由先前的外掛到現(xiàn)在正部級(jí),看來(lái)你還真要好好學(xué)習(xí)喲。
幸運(yùn)的是,盡管不同類(lèi)型瀏覽器在支持XMLHttpRequest的細(xì)節(jié)略有不同,但是,所有的瀏覽器都提供了類(lèi)似的功能,且都提供了相同的屬性和方法(外部接口),這樣就避免了兼容性問(wèn)題(用戶(hù)最害怕的問(wèn)題),一想到兼容問(wèn)題,相信很多讀者就會(huì)不寒而栗,早期的JHTML、Javascript和CSS曾經(jīng)讓多少設(shè)計(jì)師煞費(fèi)苦心。據(jù)悉,W3C組織正在努力標(biāo)準(zhǔn)化XMLHttpRequest對(duì)象,相信當(dāng)這個(gè)標(biāo)準(zhǔn)出來(lái)之后,用戶(hù)開(kāi)發(fā)Ajax的日子會(huì)好過(guò)些。
了解了XMLHttpRequest組件之后,我們?cè)倏纯此墓ぷ髟?。一般認(rèn)為XMLHttpRequest組件就是可以使用Javascript、VbScript、JScript等腳本語(yǔ)言,并通過(guò)HTTP協(xié)議傳送或接收XML數(shù)據(jù)或其他數(shù)據(jù)的一套API(外部函數(shù)庫(kù))。簡(jiǎn)單說(shuō),XMLHttpRequest組件能夠在不刷新網(wǎng)頁(yè)的基礎(chǔ)上實(shí)現(xiàn)瀏覽器與服務(wù)器的通信。
用MSDN官方解釋就是,XMLHttpRequest提供了客戶(hù)端與HTTP服務(wù)器通訊的協(xié)議。客戶(hù)端可以通過(guò)XMLHttpRequest對(duì)象向HTTP服務(wù)器發(fā)送請(qǐng)求并使用DOM(文檔對(duì)象模型)進(jìn)行處理,然后再顯示響應(yīng)的數(shù)據(jù)。
現(xiàn)在絕大多數(shù)瀏覽器都增加了對(duì)XMLHttpRequest的支持,但是在IE瀏覽器中主要使用ActiveXObject方式來(lái)創(chuàng)建XmlHttp對(duì)象,而在其他瀏覽器(如Firefox、Opera等)中,則需要通過(guò)Window.XMLHttpRequest來(lái)創(chuàng)建XMLHttpRequest對(duì)象。這為我們引用該對(duì)象增加了困難,為了兼容不同瀏覽器,一般可以使用下面代碼來(lái)實(shí)現(xiàn):
var xmlHttp;
if (window.XMLHttpRequest) { //兼容Mozilla、Safari等瀏覽器對(duì)象
xmlHttp = new XMLHttpRequest();
}
else if (window.ActiveXObject) { //兼容IE瀏覽器
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); //創(chuàng)建Msxml2.XMLHTTP控件對(duì)象
} catch (e) {
try {//創(chuàng)建Microsoft.XMLHTTP控件對(duì)象,該控件作用為獲取指定URL的內(nèi)容
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {}
}
}
是不是很簡(jiǎn)單,當(dāng)然對(duì)于第一次接觸Ajax代碼的你來(lái)說(shuō),可能感覺(jué)還是比較高深。我們簡(jiǎn)單的解釋一下:在上面代碼段中,首先定義一個(gè)變量xmlHttp,用來(lái)存儲(chǔ)XMLHttpRequest對(duì)象的實(shí)例。然后使用一個(gè)條件結(jié)構(gòu),分別判斷當(dāng)前用戶(hù)所使用的瀏覽器,并根據(jù)不同類(lèi)型瀏覽器決定定義實(shí)例的方法。對(duì)于Mozilla、Safari等非IE瀏覽器來(lái)說(shuō),如果它們支持XMLHttpRequest對(duì)象,也就是說(shuō)如果window.XMLHttpRequest為真,即存在XMLHttpRequest對(duì)象,可以使用xmlHttp = new XMLHttpRequest();方法來(lái)定義。
而對(duì)于IE瀏覽器來(lái)說(shuō),則支持ActiveXObject外掛組件來(lái)實(shí)現(xiàn),因此我們可以使用xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");或xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");的方法來(lái)實(shí)現(xiàn)。也許你還不能夠理解其中代碼的含義,筆者建議你先不妨記住這幾行代碼,或者定義為代碼塊,因?yàn)槊看味急仨氄{(diào)用。
實(shí)例化對(duì)象之后,你就可以利用該對(duì)象提供的屬性和方法完成HTTP的請(qǐng)求和響應(yīng)處理了。XMLHttpRequest對(duì)象提供的屬性如表2-1所示(皆為只讀屬性),所提供的方法如表2-2所示。
表2-1
屬性 |
說(shuō)明 |
onreadystatechange |
指定當(dāng)readyState屬性改變時(shí)的事件處理句柄 |
readyState |
返回當(dāng)前請(qǐng)求的狀態(tài) |
responseBody |
返回正文信息 |
responseStream |
以文本流的形式返回響應(yīng)信息 |
responseText |
以字符串的形式返回響應(yīng)信息 |
responseXML |
以XML數(shù)據(jù)的形式返回響應(yīng)信息 |
status |
返回當(dāng)前請(qǐng)求的HTTP狀態(tài)碼 |
statusText |
返回當(dāng)前請(qǐng)求的響應(yīng)行狀態(tài) |
表2-2
方法 |
說(shuō)明 |
abort |
取消當(dāng)前請(qǐng)求 |
getAllResponseHeaders |
獲取響應(yīng)的所有HTTP頭信息 |
getResponseHeader |
從響應(yīng)信息中獲取指定的HTTP頭信息 |
open |
創(chuàng)建一個(gè)新的HTTP請(qǐng)求,并指定此請(qǐng)求的方法、URL以及驗(yàn)證信息(用戶(hù)名/密碼) |
send |
發(fā)送請(qǐng)求到HTTP服務(wù)器并接收回應(yīng) |
setRequestHeader |
單獨(dú)指定請(qǐng)求的某個(gè)HTTP頭信息 |
XMLHttpRequest對(duì)象沒(méi)有事件,僅有14個(gè)成員對(duì)象,你可能一會(huì)兒就把它們?nèi)坑涀。遣皇呛芎?jiǎn)單,當(dāng)然要靈活使用它們,還需要你經(jīng)過(guò)實(shí)踐來(lái)學(xué)習(xí)。不過(guò)這些對(duì)象使用起來(lái)都很簡(jiǎn)單,直接調(diào)用即可,在下一節(jié)中我們將詳細(xì)講解。
創(chuàng)建一個(gè)XMLHttpRequest對(duì)象實(shí)例之后,我們就可以使用open方法創(chuàng)建一個(gè)HTTP請(qǐng)求了,說(shuō)白了就是建立一個(gè)與后臺(tái)服務(wù)器程序的連接。例如:
xmlHttp.open("GET","http://localhost/deel.aspl", false);
在上面的代碼中,利用open方法為xmlHttp對(duì)象實(shí)例建立了一個(gè)與后臺(tái)deel.asp文件的連接。
open方法中第1個(gè)參數(shù)設(shè)置發(fā)送HTTP請(qǐng)求的方法。使用GET方法表示把發(fā)送的參數(shù)附加在URL后面進(jìn)行傳遞,如果要上傳大量數(shù)據(jù)可以使用POST方法(大小寫(xiě)不敏感)。
第2個(gè)參數(shù)用來(lái)設(shè)置要打開(kāi)的服務(wù)器文件(該文件可以是任意類(lèi)型的文件),一般是ASP文件,因?yàn)槲覀兿M笈_(tái)程序來(lái)處理客戶(hù)端的請(qǐng)求。
第3個(gè)參數(shù)用來(lái)指定這個(gè)請(qǐng)求是否為異步請(qǐng)求。所謂異步就是發(fā)出請(qǐng)求之后,不需要等待服務(wù)器的響應(yīng),客戶(hù)端同時(shí)可以繼續(xù)執(zhí)行其它操作。默認(rèn)為true,表示異步請(qǐng)求。如果要發(fā)送一個(gè)同步請(qǐng)求,需要把這個(gè)參數(shù)設(shè)置為false。
如果連接的請(qǐng)求文件需要認(rèn)證,在open方法的后面還應(yīng)提供用戶(hù)名和密碼參數(shù)。
當(dāng)建立連接請(qǐng)求之后,此時(shí)你就可以使用send方法發(fā)送請(qǐng)求到HTTP服務(wù)器并接收服務(wù)器的響應(yīng)。send方法只有一個(gè)參數(shù),該參數(shù)可以攜帶請(qǐng)求數(shù)據(jù),也就是說(shuō)發(fā)送給服務(wù)器的數(shù)據(jù)。
send方法雖然能夠接收服務(wù)器的響應(yīng),但是如何把接收的數(shù)據(jù)讀取出來(lái),還需要responseBody、responseStream、responseText和responseXML屬性來(lái)實(shí)現(xiàn)。下面我們用一個(gè)簡(jiǎn)單的示例來(lái)說(shuō)明一下:
首先,請(qǐng)啟動(dòng)Dreamweaver CS3,并打開(kāi)mysite動(dòng)態(tài)站點(diǎn)(在第1章中定義的)。選擇【窗口】|【文件】菜單命令,打開(kāi)【文件】面板。在【文件】面板中新建一個(gè)文本文件(hello.txt),并在其中輸入“Hello,World”字符串。
然后,新建一個(gè)靜態(tài)頁(yè)面(如XMLHttpRequest_1.html)。打開(kāi)靜態(tài)頁(yè)面,切換到【代碼】視圖,并在任意位置(建議在頭部)輸入下面JavaScript腳本函數(shù):
<script>
function startRequest() {
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.open("GET", " hello.txt ", true);
xmlhttp.send(null);
alert(xmlhttp.responseText);
}
</script>
在這個(gè)腳本函數(shù)中,我們首先實(shí)例化XMLHttpRequest對(duì)象,并把該對(duì)象賦值給變量xmlhttp。
使用open方法與剛剛新建的hello.txt文件建立連接,然后使用send方法發(fā)送請(qǐng)求到服務(wù)器,再?gòu)姆?wù)器接收響應(yīng)的數(shù)據(jù)。上面代碼在send方法中沒(méi)有發(fā)送任何數(shù)據(jù),參數(shù)為空。
接著,使用alert方法顯示接收的文本信息。
最后,在頁(yè)面中增加一個(gè)按鈕,用來(lái)觸發(fā)該函數(shù)。這時(shí)如果你預(yù)覽該頁(yè)面,單擊其中的按鈕,則會(huì)顯示一個(gè)提示對(duì)話(huà)框,并顯示“Hello,World”字符信息(如圖2-1所示)。
<input name="" type="button" onclick="startRequest();" value="請(qǐng)求服務(wù)器" />
圖 2-1
此時(shí),你可能會(huì)問(wèn)了:靜態(tài)網(wǎng)頁(yè)咋能也可以與服務(wù)器進(jìn)行通信并處理信息呀?一般與服務(wù)器進(jìn)行通信都需要刷新頁(yè)面,這里沒(méi)有發(fā)現(xiàn)呀?呵,這一切都是XMLHttpRequest對(duì)象在后面起的作用。
我們?cè)诜?wù)器端再新建一個(gè)XML文件(如hello.xml),然后在其中輸入下面數(shù)據(jù):
<?xml version="1.0" encoding="gb2312"?>
<the>Hello,World</the>
此時(shí),如果再使用上面的代碼來(lái)讀取請(qǐng)求的信息就會(huì)返回如圖2-2所示的字符串。
圖 2-2
因此要讀取XML數(shù)據(jù),就應(yīng)該使用responseXML屬性來(lái)顯示。代碼如下:
<script>
function startRequest() {
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.open("GET", "hello.xml", true);
xmlhttp.send(null);
alert(xmlhttp.responseXML);
}
</script>
但是如果直接使用上面的xmlhttp.responseXML屬性來(lái)讀取服務(wù)器響應(yīng)的信息,會(huì)顯示如圖2-3所示的提示信息,說(shuō)明所讀取的是一個(gè)對(duì)象(即擁有一定數(shù)據(jù)結(jié)構(gòu)的變量),而無(wú)法顯示任何數(shù)據(jù)。
圖 2-3
這時(shí),你可以使用XML DOM提供的方法和屬性提取其中的字符串。代碼如下:
<script>
function startRequest() {
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.open("GET", "hello.xml", true);
xmlhttp.send(null);
alert(xmlhttp.responseXML.getElementsByTagName("the")[0].firstChild.data);
}
</script>
在上面代碼中使用DOM提供的getElementsByTagName方法獲取the節(jié)點(diǎn),然后再定位第一個(gè)the節(jié)點(diǎn)的子節(jié)點(diǎn)內(nèi)容(有關(guān)DOM文檔對(duì)象模型的方法和屬性請(qǐng)參閱相關(guān)圖書(shū),本書(shū)不作深入研究)。
上面實(shí)例演示了如何與服務(wù)器端指定文件建立連接,并獲取服務(wù)器的響應(yīng)數(shù)據(jù)。但是沒(méi)有涉及到如何把客戶(hù)端數(shù)據(jù)發(fā)送到服務(wù)器端以實(shí)現(xiàn)數(shù)據(jù)的交互。實(shí)現(xiàn)把數(shù)據(jù)從客戶(hù)端向服務(wù)器端傳遞有兩種方法:
第一種方法,通過(guò)查詢(xún)字符串進(jìn)行參數(shù)傳遞。
首先在服務(wù)器端新建一個(gè)ASP文件(hello.asp),在該文件中輸入下面代碼:
<%
name = Request.QueryString("name")
Response.AddHeader "Content-Type","text/html;charset=gb2312"
Response.Write "Hello:" & name
%>
其中第一行表示獲取客戶(hù)端傳遞的查詢(xún)字符串參數(shù)值。什么是查詢(xún)字符串,以及如何獲取客戶(hù)提交的數(shù)據(jù)?我們將在第12章中詳細(xì)進(jìn)行講解,讀者可以簡(jiǎn)單了解一下即可。
第2行代碼表示設(shè)置響應(yīng)內(nèi)容的字符編碼(詳細(xì)講解可以參閱第7章內(nèi)容),這里設(shè)置服務(wù)器準(zhǔn)備響應(yīng)客戶(hù)端的內(nèi)容為中文簡(jiǎn)體編碼。
第3行代碼表示把響應(yīng)信息返回給客戶(hù)端。
然后,在前臺(tái)頁(yè)面中修改代碼如下(XMLHttpRequest_4.html):
<script>
function startRequest() {
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.open("GET", "hello.asp?name=zhuyinhong", true);
xmlhttp.send();
alert(xmlhttp.responseText);
}
</script>
<input name="" type="button" onclick="startRequest();" value="請(qǐng)求服務(wù)器" />
看見(jiàn)沒(méi)有,所要傳遞的參數(shù)被附加在URL的后面,前面補(bǔ)加“?”分隔符。這就是GET方法發(fā)送數(shù)據(jù)。顯示效果如圖2-4所示。
圖 2-4
上面所舉示例是以GET方法向服務(wù)器傳遞數(shù)據(jù),下面我們?cè)賴(lài)L試如何使用POST方法向服務(wù)器傳遞數(shù)據(jù)。
首先,在服務(wù)器中新建一個(gè)ASP文件(hello1.asp),在該文件中獲取客戶(hù)端傳遞的值,并返回響應(yīng),代碼如下:
<%@Language="VBScript" CodePage="936"%>
<%
name = Request.Form("name")
Response.AddHeader "Content-Type","text/html;charset=gb2312"
Response.Write "Hello:" & name
%>
為了簡(jiǎn)化學(xué)習(xí)難度,這里依然以上面示例為基礎(chǔ)進(jìn)行講解,上下兩個(gè)示例的后臺(tái)處理文件基本相同,不同點(diǎn)就是接收客戶(hù)端數(shù)據(jù)的方法略有不同,在后面的章節(jié)中我們將詳細(xì)講解如何進(jìn)行數(shù)據(jù)的接收和處理(參閱第12章內(nèi)容)。
然后新建一個(gè)靜態(tài)頁(yè)面(XMLHttpRequest_5.html),請(qǐng)輸入如下腳本代碼:
<script>
function startRequest() {
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.open("POST", "hello1.asp", false);
xmlhttp.setRequestHeader('Content-type','application/x-www-form-urlencoded');
xmlhttp.send("name=zhuyinhong");
alert(xmlhttp.responseText);
}
</script>
<input name="" type="button" onclick="startRequest();" value="請(qǐng)求服務(wù)器" />
你可以看到,該示例代碼的基本結(jié)構(gòu)與上面示例的代碼結(jié)構(gòu)沒(méi)有太大變化。但是在open方法中設(shè)置為POST方法。最關(guān)鍵的是增加了setRequestHeader方法,該方法專(zhuān)門(mén)用來(lái)設(shè)置請(qǐng)求的消息頭,這里必須定義請(qǐng)求消息的內(nèi)容類(lèi)型為“application/x-www-form-urlencoded”,它表示傳遞是的表單值,如果不明白可以先不管,一般使用POST發(fā)送表單值時(shí)都必須設(shè)置該選項(xiàng)。
然后在send方法中附加要傳遞的值,該值是一個(gè)或多個(gè)“名/值”對(duì),多個(gè)“名/值”對(duì)之間使用“&”分隔符進(jìn)行分隔。這樣在服務(wù)器端ASP程序就可以利用Request.Form()方法捕獲所傳遞過(guò)來(lái)的值。
在“名/值”對(duì)中,“名”可以為表單域的名稱(chēng)(與表單域相對(duì)應(yīng)),“值”可以是固定的值,也可以是一個(gè)變量。如果是變量,你就可以把表單域內(nèi)包含的值直接傳遞給變量,再由變量負(fù)責(zé)把數(shù)據(jù)傳遞給服務(wù)器端。這時(shí)你還必須把open方法中的第3個(gè)參數(shù)設(shè)置為false,即關(guān)閉異步通信功能。
此時(shí)預(yù)覽頁(yè)面,單擊提交按鈕則會(huì)顯示如圖2-4所示效果。
由于XMLHttpRequest對(duì)象是不通過(guò)刷新頁(yè)面來(lái)傳遞數(shù)據(jù)的,很多時(shí)候用戶(hù)是無(wú)法準(zhǔn)確判斷數(shù)據(jù)是否已經(jīng)發(fā)送給服務(wù)器,或者服務(wù)器是否已經(jīng)返回了數(shù)據(jù)?
為此,XMLHttpRequest對(duì)象提供了很多屬性和方法來(lái)實(shí)時(shí)跟蹤數(shù)據(jù)傳輸?shù)臓顟B(tài),這樣用戶(hù)就可以很方便的使用腳本來(lái)處理和控制HTTP的請(qǐng)求和響應(yīng)。
當(dāng)XMLHttpRequest對(duì)象把一個(gè)HTTP請(qǐng)求發(fā)送到服務(wù)器端時(shí),要經(jīng)歷幾個(gè)狀態(tài),這時(shí)你可以調(diào)用XMLHttpRequest對(duì)象的readyState屬性來(lái)判斷請(qǐng)求和響應(yīng)已經(jīng)進(jìn)入了哪一步。
readyState屬性包括5個(gè)取值(如表2-2所示)。借助readyState屬性的這些取值,你就可以判斷請(qǐng)求和響應(yīng)是否成功。
表2-2
屬性值 |
說(shuō)明 |
0(未初始化) |
對(duì)象已建立,但是尚未初始化,即尚未調(diào)用open方法 |
1(初始化) |
對(duì)象已建立,尚未調(diào)用send方法 |
2(發(fā)送數(shù)據(jù)) |
send方法已調(diào)用,但是當(dāng)前的狀態(tài)及HTTP頭部信息未知 |
3(數(shù)據(jù)傳送中) |
服務(wù)器已接收部分?jǐn)?shù)據(jù),因?yàn)轫憫?yīng)及HTTP頭部信息不全,這時(shí)通過(guò)responseBody和responseText獲取部分?jǐn)?shù)據(jù)會(huì)出現(xiàn)錯(cuò)誤 |
4(完成) |
數(shù)據(jù)接收完畢,此時(shí)可以通過(guò)responseBody和responseText獲取完整的響應(yīng)數(shù)據(jù) |
如果readyState屬性返回值為4,則說(shuō)明響應(yīng)完畢,那么你就可以放心的讀取返回的數(shù)據(jù)了。但是在實(shí)際開(kāi)發(fā)中,我們習(xí)慣上把處理響應(yīng)的代碼封裝在一個(gè)函數(shù)內(nèi),然后借用onreadystatechange事件屬性來(lái)觸發(fā)該函數(shù)。onreadystatechange屬性表示當(dāng)readyState屬性改變時(shí)將調(diào)用指定函數(shù)。
另外,XMLHttpRequest對(duì)象還提供了status屬性,利用該屬性可以更精確的跟蹤HTTP請(qǐng)求和響應(yīng)的狀態(tài)以及細(xì)節(jié)。由于status屬性包含的值眾多,感興趣的讀者可以參考本書(shū)光盤(pán)中提供的參考手冊(cè),或者參考其他資料。不過(guò)請(qǐng)記住,如果當(dāng)status屬性取值為200或?yàn)?時(shí),服務(wù)器響應(yīng)完畢,我們常利用這個(gè)屬性值來(lái)判斷響應(yīng)是否成功。
例如,我們?cè)谏厦媸纠A(chǔ)上再定義一個(gè)腳本函數(shù)(復(fù)制上面示例文件為XMLHttpRequest_6.html):
function handleStateChange(){
if(xmlHttp.readyState == 4){
if (xmlHttp.status == 200 || xmlHttp.status == 0){
alert(xmlhttp.responseText);
}
}
}
不要忘記在腳本前面定義一個(gè)公共變量xmlHttp。然后,修改startRequest()函數(shù):
var xmlhttp;
function startRequest() {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.open("POST", "hello1.asp", false);
xmlhttp.onreadystatechange = handleStateChange;
xmlhttp.setRequestHeader('Content-type','application/x-www-form-urlencoded');
xmlhttp.send("name=zhuyinhong");
}
你可以看到,這里沒(méi)有把讀取響應(yīng)數(shù)據(jù)的代碼行放在startRequest()函數(shù)的后面,而是單獨(dú)放在一個(gè)函數(shù)中,然后通過(guò)onreadystatechange事件屬性來(lái)調(diào)用這個(gè)事件句柄函數(shù)。
onreadystatechange屬性應(yīng)該位于send方法前面,否則不會(huì)響應(yīng)。最后預(yù)覽的結(jié)果如圖2-4所示效果。
如對(duì)本文有疑問(wèn),請(qǐng)?zhí)峤坏浇涣髡搲?,廣大熱心網(wǎng)友會(huì)為你解答??! 點(diǎn)擊進(jìn)入論壇