XMLHttpRequest對象是當今所有AJAX和Web 2.0應用程式的技術基礎。儘管軟體經銷商和開源社團現在都在提供各種AJAX架構以進一步簡化XMLHttpRequest對象的使用;但是,我們仍然很有必要理解這個對象的詳細工作機制。 一、 引言 非同步JavaScript與XML(AJAX)是一個專用術語,用於實現在用戶端指令碼與伺服器之間的資料互動過程。這一技術的優點在於,它向開發人員提供了一種從Web伺服器檢索資料而不必把使用者當前正在觀察的頁面回饋給伺服器。與現代瀏覽器的通過存取瀏覽器DOM結構的編程代碼(JavaScript)動態地改變被顯示內容的支援相配合,AJAX讓開發人員在瀏覽器端更新被顯示的HTML內容而不必重新整理頁面。換句話說,AJAX可以使基於瀏覽器的應用程式更具互動性而且更類似傳統型傳統型應用程式。 Google的Gmail和Outlook Express就是兩個使用AJAX技術的我們所熟悉的例子。而且,AJAX可以用於任何用戶端指令碼語言中,這包括JavaScript,Jscript和VBScript。 AJAX利用一個構建到所有現代瀏覽器內部的對象-XMLHttpRequest-來實現發送和接收HTTP請求與響應資訊。一個經由XMLHttpRequest對象發送的HTTP請求並不要求頁面中擁有或回寄一個<form>元素。AJAX中的"A"代表了"非同步",這意味著XMLHttpRequest對象的send()方法可以立即返回,從而讓Web頁面上的其它HTML/JavaScript繼續其瀏覽器端處理而由伺服器處理HTTP請求並發送響應。儘管預設情況下請求是非同步進行的,但是,你可以選擇發送同步請求,這將會暫停其它Web頁面的處理,直到該頁面接收到伺服器的響應為止。 微軟在其Internet Explorer(IE) 5中作為一個ActiveX對象形式引入了XMLHttpRequest對象。其他的認識到這一對象重要性的瀏覽器製造商也都紛紛在他們的瀏覽器內實現了XMLHttpRequest對象,但是作為一個本地JavaScript對象而不是作為一個ActiveX對象實現。而如今,在認識到實現這一類型的價值及安全性特徵之後,微軟已經在其IE 7中把XMLHttpRequest實現為一個視窗對象屬性。幸運的是,儘管其實現(因而也影響到調用方式)細節不同,但是,所有的瀏覽器實現都具有類似的功能,並且實質上是相同方法。目前,W3C組織正在努力進行XMLHttpRequest對象的標準化,並且已經發行了有關該W3C規範的一個草案。 本文將對XMLHttpRequest對象API進行詳細討論,並將解釋其所有的屬性和方法。 二、 XMLHttpRequest對象的屬性和事件 XMLHttpRequest對象暴露各種屬性、方法和事件以便於指令碼處理和控制HTTP請求與響應。下面,我們將對此展開詳細的討論。 readyState屬性 當XMLHttpRequest對象把一個HTTP請求發送到伺服器時將經曆若干種狀態:一直等待直到請求被處理;然後,它才接收一個響應。這樣以來,指令碼才正確響應各種狀態-XMLHttpRequest對象暴露一個描述對象的目前狀態的readyState屬性,如表格1所示。 表格1.XMLHttpRequest對象的ReadyState屬性值列表。 ReadyState取值 描述 0 描述一種"未初始化"狀態;此時,已經建立一個XMLHttpRequest對象,但是還沒有初始化。 1 描述一種"發送"狀態;此時,代碼已經調用了XMLHttpRequest open()方法並且XMLHttpRequest已經準備好把一個請求發送到伺服器。 2 描述一種"發送"狀態;此時,已經通過send()方法把一個請求發送到伺服器端,但是還沒有收到一個響應。 3 描述一種"正在接收"狀態;此時,已經接收到HTTP回應標頭部資訊,但是訊息體部分還沒有完全接收結束。 4 描述一種"已載入"狀態;此時,響應已經被完全接收。 onreadystatechange事件 無論readyState值何時發生改變,XMLHttpRequest對象都會激發一個readystatechange事件。其中,onreadystatechange屬性接收一個EventListener值-向該方法指示無論readyState值何時發生改變,該對象都將啟用。 responseText屬性 這個responseText屬性包含用戶端接收到的HTTP響應的常值內容。當readyState值為0、1或2時,responseText包含一個Null 字元串。當readyState值為3(正在接收)時,響應中包含用戶端還未完成的響應資訊。當readyState為4(已載入)時,該responseText包含完整的響應資訊。 responseXML屬性 此responseXML屬性用於當接收到完整的HTTP響應時(readyState為4)描述XML響應;此時,Content-Type頭部指定MIME(媒體)類型為text/xml,application/xml或以+xml結尾。如果Content-Type頭部並不包含這些媒體類型之一,那麼responseXML的值為null。無論何時,只要readyState值不為4,那麼該responseXML的值也為null。 其實,這個responseXML屬性值是一個文檔介面類型的對象,用來描述被分析的文檔。如果文檔不能被分析(例如,如果文檔不是良構的或不支援文檔相應的字元編碼),那麼responseXML的值將為null。 status屬性 這個status屬性描述了HTTP狀態碼,而且其類型為short。而且,僅當readyState值為3(正在接收中)或4(已載入)時,這個status屬性才可用。當readyState的值小於3時試圖存取status的值將引發一個異常。 statusText屬性 這個statusText屬性描述了HTTP狀態碼文本;並且僅當readyState值為3或4才可用。當readyState為其它值時試圖存取statusText屬性將引發一個異常。 三、 XMLHttpRequest對象的方法 XMLHttpRequest對象提供了各種方法用於初始化和處理HTTP請求,下列將逐個展開詳細討論。 abort()方法 你可以使用這個abort()方法來暫停與一個XMLHttpRequest對象相聯絡的HTTP請求,從而把該對象複位到未初始化狀態。 open()方法 你需要調用open(DOMString method,DOMString uri,boolean async,DOMString username,DOMString password)方法初始化一個XMLHttpRequest對象。其中,method參數是必須提供的-用於指定你想用來發送請求的HTTP方法(GET,POST,PUT,DELETE或HEAD)。為了把資料發送到伺服器,應該使用POST方法;為了從伺服器端檢索資料,應該使用GET方法。另外,uri參數用於指定XMLHttpRequest對象把請求發送到的伺服器相應的URI。藉助於window.document.baseURI屬性,該uri被解析為一個絕對的URI-換句話說,你可以使用相對的URI-它將使用與瀏覽器解析相對的URI一樣的方式被解析。async參數指定是否請求是非同步-預設值為true。為了發送一個同步請求,需要把這個參數設定為false。對於要求認證的伺服器,你可以提供可選的使用者名稱和口令參數。在調用open()方法後,XMLHttpRequest對象把它的readyState屬性設定為1(開啟)並且把responseText、responseXML、status和statusText屬性複位到它們的初始值。另外,它還複位要求標頭部。注意,如果你調用open()方法並且此時readyState為4,則XMLHttpRequest對象將複位這些值。 send()方法 在通過調用open()方法準備好一個請求之後,你需要把該請求發送到伺服器。僅當readyState值為1時,你才可以調用send()方法;否則的話,XMLHttpRequest對象將引發一個異常。該請求被使用提供給open()方法的參數發送到伺服器。當async參數為true時,send()方法立即返回,從而允許其它用戶端指令碼處理繼續。在調用send()方法後,XMLHttpRequest對象把readyState的值設定為2(發送)。當伺服器響應時,在接收訊息體之前,如果存在任何訊息體的話,XMLHttpRequest對象將把readyState設定為3(正在接收中)。當請求完成載入時,它把readyState設定為4(已載入)。對於一個HEAD類型的請求,它將在把readyState值設定為3後再立即把它設定為4。 send()方法使用一個可選的參數-該參數可以包含可變類型的資料。典型地,你使用它並通過POST方法把資料發送到伺服器。另外,你可以顯式地使用null參數調用send()方法,這與不用參數調用它一樣。對於大多數其它的資料類型,在調用send()方法之前,應該使用setRequestHeader()方法(見後面的解釋)先設定Content-Type頭部。如果在send(data)方法中的data參數的類型為DOMString,那麼,資料將被編碼為UTF-8。如果資料是Document類型,那麼將使用由data.xmlEncoding指定的編碼序列化該資料。 setRequestHeader()方法 該setRequestHeader(DOMString header,DOMString value)方法用來佈建要求的頭部資訊。當readyState值為1時,你可以在調用open()方法後調用這個方法;否則,你將得到一個異常。 getResponseHeader()方法 getResponseHeader(DOMString header,value)方法用於檢索響應的頭部值。僅當readyState值是3或4(換句話說,在回應標頭部可用以後)時,才可以調用這個方法;否則,該方法返回一個Null 字元串。 getAllResponseHeaders()方法 該getAllResponseHeaders()方法以一個字串形式返回所有的回應標頭部(每一個頭部占單獨的一行)。如果readyState的值不是3或4,則該方法返回null。 四、 發送請求 在AJAX中,許多使用XMLHttpRequest的請求都是從一個HTML事件(例如一個調用JavaScript函數的按鈕點擊(onclick)或一個按鍵(onkeypress))中被初始化的。AJAX支援包括表單校正在內的各種應用程式。有時,在填充表單的其它內容之前要求校正一個唯一的表單域。例如要求使用一個唯一的UserID來註冊表單。如果不是使用AJAX技術來校正這個UserID域,那麼整個表單都必須被填充和提交。如果該UserID不是有效,這個表單必須被重新提交。例如,一個相應於一個要求必須在伺服器端進行校正的Catalog ID的表單域可能按下列形式指定:<form name="validationForm" action="validateForm" method="post"> <table> <tr><td>Catalog Id:</td> <td> <input type="text" size="20" id="catalogId" name="catalogId" autocomplete="off" onkeyup="sendRequest()"> </td> <td><div id="validationMessage"></div></td> </tr> </table></form> 前面的HTML使用validationMessage div來顯示相應於這個輸入欄位Catalog Id的一個校正訊息。onkeyup事件調用一個JavaScript sendRequest()函數。這個sendRequest()函數建立一個XMLHttpRequest對象。建立一個XMLHttpRequest對象的過程因瀏覽器實現的不同而有所區別。如果瀏覽器支援XMLHttpRequest對象作為一個視窗屬性(所有普通的瀏覽器都是這樣的,除了IE 5和IE 6之外),那麼,代碼可以調用XMLHttpRequest的構造器。如果瀏覽器把XMLHttpRequest對象實現為一個ActiveXObject對象(就象在IE 5和IE 6中一樣),那麼,代碼可以使用ActiveXObject的構造器。下面的函數將調用一個init()函數,它負責檢查並決定要使用的適當的建立方法-在建立和返回對象之前。<script type="text/javascript"> function sendRequest(){ var xmlHttpReq=init(); function init(){ if (window.XMLHttpRequest) { return new XMLHttpRequest(); } else if (window.ActiveXObject) { return new ActiveXObject("Microsoft.XMLHTTP"); } } </script> 接下來,你需要使用Open()方法初始化XMLHttpRequest對象-指定HTTP方法和要使用的伺服器URL。var catalogId=encodeURIComponent(document.getElementById("catalogId").value); xmlHttpReq.open("GET", "validateForm?catalogId=" + catalogId, true); 預設情況下,使用XMLHttpRequest發送的HTTP請求是非同步進行的,但是你可以顯式地把async參數設定為true,如上面所展示的。 在這種情況下,對URL validateForm的調用將啟用伺服器端的一個servlet,但是你應該能夠注意到伺服器端技術不是根本性的;實際上,該URL可能是一個ASP,ASP.NET或PHP頁面或一個Web服務-這無關緊要,只要該頁面能夠返回一個響應-指示CatalogID值是否是有效-即可。因為你在作一個非同步呼叫,所以你需要註冊一個XMLHttpRequest對象將調用的回調事件處理器-當它的readyState值改變時調用。記住,readyState值的改變將會激發一個readystatechange事件。你可以使用onreadystatechange屬性來註冊該回調事件處理器。xmlHttpReq.onreadystatechange=processRequest; 然後,我們需要使用send()方法發送該請求。因為這個請求使用的是HTTP GET方法,所以,你可以在不指定參數或使用null參數的情況下調用send()方法。xmlHttpReq.send(null);五、 處理請求 在這個樣本中,因為HTTP方法是GET,所以在伺服器端的接收servlet將調用一個doGet()方法,該方法將檢索在URL中指定的catalogId參數值,並且從一個資料庫中檢查它的有效性。 本文樣本中的這個servlet需要構造一個發送到用戶端的響應;而且,這個樣本返回的是XML類型,因此,它把響應的HTTP內容類型設定為text/xml並且把Cache-Control頭部設定為no-cache。設定Cache-Control頭部可以阻止瀏覽器簡單地從緩衝中重載頁面。public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ... ... response.setContentType("text/xml"); response.setHeader("Cache-Control", "no-cache"); } 來自於伺服器端的響應是一個XML DOM對象,此對象將建立一個XML字串-其中包含要在用戶端進行處理的指令。另外,該XML字串必須有一個根項目。out.println("<catalogId>valid</catalogId>"); 【注意】XMLHttpRequest對象的設計目的是為了處理由普通文本或XML組成的響應;但是,一個響應也可能是另外一種類型,如果使用者代理程式(UA)支援這種內容類型的話。 當請求狀態改變時,XMLHttpRequest對象調用使用onreadystatechange註冊的事件處理器。因此,在處理該響應之前,你的事件處理器應該首先檢查readyState的值和HTTP狀態。當請求完成載入(readyState值為4)並且響應已經完成(HTTP狀態為"OK")時,你就可以調用一個JavaScript函數來處理該響應內容。下列指令碼負責在響應完成時檢查相應的值並調用一個processResponse()方法。function processRequest(){ if(xmlHttpReq.readyState==4){ if(xmlHttpReq.status==200){ processResponse(); } } } 該processResponse()方法使用XMLHttpRequest對象的responseXML和responseText屬性來檢索HTTP響應。如上面所解釋的,僅當在響應的媒體類型是text/xml,application/xml或以+xml結尾時,這個responseXML才可用。這個responseText屬性將以普通文本形式返迴響應。對於一個XML響應,你將按如下方式檢索內容:var msg=xmlHttpReq.responseXML; 藉助於儲存在msg變數中的XML,你可以使用DOM方法getElementsByTagName()來檢索該元素的值:var catalogId=msg.getElementsByTagName("catalogId")[0].firstChild.nodeValue; 最後,通過更新Web頁面的validationMessage div中的HTML內容並藉助於innerHTML 屬性,你可以測試該元素值以建立一個要顯示的訊息:if(catalogId=="valid"){ var validationMessage = document.getElementById("validationMessage"); validationMessage.innerHTML = "Catalog Id is Valid"; } else { var validationMessage = document.getElementById("validationMessage"); validationMessage.innerHTML = "Catalog Id is not Valid"; } 六、 小結 上面就是XMLHttpRequest對象使用的所有細節實現。通過不必把Web頁面寄送到伺服器而實現資料傳送,XMLHttpRequest對象為用戶端與伺服器之間提供了一種動態互動能力。你可以使用JavaScript啟動一個請求並處理相應的傳回值,然後使用瀏覽器的DOM方法更新頁面中的資料。 |