作者:Naveen Balani, 技術架構師, Webify Solutions ?????????Rajeev Hathi, 高級系統分析員, Satyam 計算機有限公司
異步 JavaScript 和 XML(Asynchronous JavaScript with XML,AJAX)支持動態、異步的 Web 體驗,卻不需要頁面刷新。在本教程中,將跟隨一個示例圖書訂購應用程序的構建,學習構建基于 AJAX 的 Web 應用程序 —— 擁有實時驗證,卻不用頁面刷新。
開始之前
關于本教程
在本教程中,我解釋了如何開發和設計基于異步 JavaScript 和 XML(或者叫做 AJAX)的 Web 應用程序。您將構建一個基于 Web 的、提供實時驗證和頁面刷新、為用戶提供有效和平滑的用戶交互的示例圖書訂購應用程序。
|
?
前提條件
我們將使用 Tomcat 來運行 AJAX 應用程序。Tomcat 是 Java Servlet 和 Java 服務器頁面技術的參考實現使用的 servlet 容器。請從 Jakarta Site 下載 Download jakarta-tomcat-5.0.28.exe 并運行它把 Tomcat 安裝到喜歡的位置 —— 例如 c:\tomcat5.0。
請下載本教程的源代碼和 Web 應用程序(在 wa-ajax-Library.war 中)。
AJAX 介紹
AJAX 基礎
AJAX 支持動態、異步的 Web 體驗,不需要頁面刷新。它集成了以下技術:
- XHTML 和 CSS 提供了基于標準的表示。
- 文檔對象模型(DOM)提供了動態顯示和交互。
- XML 和 XSLT 提供了數據交換和操縱。
-
XMLHttpRequest 提供了異步數據檢索。
- JavaScript 把每一樣東西綁定在一起。
AJAX 技術的核心是 JavaScript 對象:XMLHttpRequest 。這個對象是通過瀏覽器實現提供的 —— 先是通過 Internet Explorer 5.0 然后通過 Mozilla 兼容的瀏覽器。請進一步觀察這個對象。
XMLHttpRequest
使用 XMLHttpRequest ,可以用 JavaScript 發出到服務器的請求,并在不阻塞用戶的情況下處理響應。在創建 Web 站點并用 XMLHttpRequest 在客戶機瀏覽器上無刷新地執行屏幕更新的同時,它還提供了更多靈活性和豐富的用戶體驗。
XMLHttpRequest 應用程序的示例包括 Google 的 Gmail 服務、Google 的 Suggest 動態查詢界面以及 MapQuest 的動態地圖界面。在下一節,在演示圖書訂購應用程序的設計和實現時,詳細介紹了如何使用 XMLHttpRequest 對象。
應用程序設計
應用程序的元素
基于 Web 的示例圖書訂購應用程序包含以下用 AJAX 實現的客戶端函數:
- 訂購 ID 驗證
- 一個 View Authors 列表
- 一個 View Publishers 列表
這里的目的是介紹 Web 頁面中的實時驗證 和頁面刷新 如何讓客戶交互更平滑、更有效。
應用程序的結構
圖 1 的圖表說明了示例圖書訂購應用程序的設計架構:
圖 1. AJAX 架構
應用程序是一個使用 Java 服務器頁面(JSP)技術開發的單一 Web 頁面。用戶可以用 Web 瀏覽器(例如 Microsoft? Internet Explorer)調用 Web 頁面并輸入應用程序要實時驗證的訂購 ID。在 ID 異步驗證的時候,用戶可以輸入更多信息。用戶可以根據作者 或出版者 查看圖書的書名。屏幕會根據用戶的選擇填充作者列表 或出版者列表。根據選擇,書名列表 會被填充。所有這些列表都是實時填充的 —— 換句話說,頁面沒有刷新,但是數據仍然來自后臺層。我們把這種現象叫做實時刷新。
從 圖 1 可以看出,XMLHttpRequest JavaScript 對象幫助進行實時異步處理。該對象采用 XML 格式通過 HTTP 對位于 Web 容器內的 LibraryServlet 發出請求。然后 servlet 查詢數據庫、提取數據并發送回客戶機,還是采用 XML 格式通過 HTTP 進行傳送。請求和響應都是在沒有刷新頁面的情況下實時發生的。
就是這一點使得 AJAX 如此強大。用戶不需要等候頁面重新載入才能完成,因為這里沒有頁面重載。
在 下一節 中,我將演示如何根據這個設計來實現圖書訂購應用程序。我要介紹代碼并做一些分析。(要得到本教程的示例代碼,請 下載文件 x-ajax-library.war。)
實現應用程序
用 AJAX 實現應用程序
在這一節,我們研究示例圖書訂購應用程序的代碼,并進一步查看每個基于 AJAX 的 Javascript 組件:
查看代碼:驗證訂購 ID
先從驗證訂購 ID 的函數 <input type="text" name="subscriptionID" onblur="validate(this.form)"/> 開始。這個代碼生成文本字段,用戶可以在里面輸入訂購 ID。用戶輸入 ID 并移到表單的下一個字段時,觸發 onBlur 事件。這個事件調用 JavaScript 函數 validate() :
var req;
function validate(formObj) {
init();
req.onreadystatechange = subscriptionValidator;
req.send("subscriptionID=" + formObj.subscriptionID.value);
}
|
validate() 函數接受 formObj 作為參數。它首先調用 init() 函數:
function init() {
if (window.XMLHttpRequest) {
req = new XMLHttpRequest();
} else if (window.ActiveXObject) {
req = new ActiveXObject("Microsoft.XMLHTTP");
}
var url = "/Library/LibraryServlet";
req.open("POST", url, true);
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
}
|
查看代碼: init()
現在看 init() 函數的工作(我們把代碼分成幾部分):
if (window.XMLHttpRequest) {
req = new XMLHttpRequest();
} else if (window.ActiveXObject) {
req = new ActiveXObject("Microsoft.XMLHTTP");
}
|
init() 函數首先創建 XMLHttpRequest 對象。這個請求對象是 AJAX 的核心。它以 XML 格式發送和接收請求。這段代碼檢查瀏覽器對 XMLHttpRequest 對象的支持(多數瀏覽器都支持它)。如果使用 Microsoft Internet Explorer 5.0 以上版本,那么就執行第二個條件。
req.open("POST", url, true);
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
代碼創建了 XMLHttpRequest 對象之后,需要設置某些請求屬性。在前面的代碼中,第一行設置請求方法、請求 URL 和請求的類型(是否異步)。它通過調用 XMLHttpRequest 對象上的 open() 方法做這件事。
這里我們要使用 POST 方法。理想情況下,當需要在服務器上修改狀態時,請使用 POST 。我們的應用程序并不修改狀態,但我們仍然傾向于使用 POST 。url 是要執行的 servlet 的 URL。true 表明我們要異步地執行請求。
對于 POST 方法,我們需要設置 Content-Type 這個請求頭。對于 GET 方法來說不需要這個設置。
function validate(formObj) {
init();
req.onreadystatechange = subscriptionValidator;
req.send("subscriptionID=" + formObj.subscriptionID.value);
}
|
查看代碼:回調句柄 1
繼續查看驗證方法,下面把 subscriptionValidator 回調句柄分配給 onreadystatechange ,請求狀態的每個變化都會觸發它。
這個回調句柄 都負責什么呢?因為正在異步地處理請求,所以需要一個回調句倆,從服務器返回完整響應的時候調用它 —— 回調句柄是對訂購 ID 進行驗證的地方(也就是編寫實際的驗證代碼的地方)。
句柄充當偵聽器。它一直等待響應完成。(稍后 將詳細介紹句柄代碼 。)為了發送請求,最后一行調用了 send() 方法。請求以名稱=值 對的形式發送。對于 GET 方法,請求作為 URL 的一部分發送,所以 send() 方法被傳遞了一個空參數。
請求被發送到 servlet。servlet 處理請求并實時地發回響應。這就是 servlet 處理請求的方式。下一個代碼段表示了 LibraryServlet 的 doPost() 方法。
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
String ID = null;
ID = req.getParameter("subscriptionID");
if (ID != null) {
String status = "<message>" + this.validID(ID) + "</message>";
this.writeResponse(resp, status);
}
}
|
查看代碼:回調句柄 2
doPost() 方法從請求參數得到 subscriptionID 。為了驗證 ID,它調用 validID() 方法。這個方法驗證 ID,如果 ID 正確,則返回 true ,否則返回 false 。它用 XML 格式構造返回狀態,并調用 writeResponse() 方法來寫響應。現在來看 writeResponse() 方法。
public void writeResponse(HttpServletResponse resp, String output) throws IOException {
resp.setContentType("text/xml");
resp.setHeader("Cache-Control", "no-cache");
resp.getWriter().write(output);
}
|
響應用 XML 格式發送。第一行設置響應的內容類型為 text/xml 。第二行把頭 Cache-Control 的值設為 no-cache 。這個值是必需的。AJAX 要求響應的輸出不能被瀏覽器緩存。最后一行調用 getWriter().write() 方法來寫響應。
查看代碼:回調句柄 3
請求由 servlet 處理,響應被發送回客戶機。請記住,所有這些都在后臺發生,沒有頁面刷新。現在 前面 討論過的回調句柄方法會處理并解析響應:
function subscriptionValidator() {
if (req.readystate == 4) {
if (req.status == 200) {
var messageObj = req.responseXML.getElementsByTagName("message")[0];
var message = messageObj.childNodes[0].nodeValue;
if (message == "true") {
msg.innerHTML = "Subscription is valid";
document.forms[0].order.disabled = false;
} else {
msg.innerHTML = "Subscription not valid";
document.forms[0].order.disabled = true;
}
}
}
}
|
查看代碼:回到 XMLHttpRequest
如前所述,XMLHttpRequest 對象是構造和發送請求的核心對象。它也負責讀取和解析從服務器返回的響應。請看下面幾部分代碼。
if (req.readystate == 4) {
if (req.status == 200) {
|
前面的代碼檢查請求的狀態。如果請求處在就緒狀態,就讀取和解析響應。
就緒狀態是什么意思呢?當請求對象的屬性 readystate 的值是 4 時,就意味著客戶機接收到了響應而且接收完成。下面我們檢查請求的狀態(響應是正常頁面還是錯誤頁面)。為了保證響應正常,要檢查狀態的值是否為 200 。如果 status 的值是 200 ,就會處理響應。
var messageObj = req.responseXML.getElementsByTagName("message")[0];
var message = messageObj.childNodes[0].nodeValue;
if (message == "true") {
msg.innerHTML = "Subscription is valid";
document.forms[0].order.disabled = false;
} else {
msg.innerHTML = "Subscription not valid";
document.forms[0].order.disabled = true;
} }
|
接下來,請求對象通過調用 responseXML 屬性讀取響應。請注意 servlet 用 XML 格式發送回響應,所以我們使用 responseXML 。如果響應是以文本格式發送的,那么可以使用 responseText 屬性。
在這個示例中,我們處理 XML。servlet 把響應構建在一個 <message> 標記中。要解析這個 XML 標記,請在 XMLHttpRequest 對象的 responseXML 屬性上調用 getElementsByTagName() 方法。它得到標記的名稱以及標記的子值。根據解析到的值,格式化響應并用 HTML 改寫。
現在就完成了對訂購 ID 的驗證,沒有頁面刷新。
查看代碼:查看作者、出版者和書名
其他的功能 —— 查看作者、查看出版者 和查看書名 —— 工作的方式類似。只是需要為每個功能定義獨立的句柄:
function displayList(field) {
init();
titles.innerHTML = " ";
req.onreadystatechange = listHandler;
req.send("select=" + escape(field));
}
function displayTitles(formObj) {
init();
var index = formObj.list.selectedIndex;
var val = formObj.list.options[index].value;
req.onreadystatechange = titlesHandler;
req.send("list=" + val);
}
|
請記住,示例應用程序允許用戶根據作者和出版者查看書名。所以顯示的或者是作者列表 或者是出版者列表。在這類場景中,應用程序只能根據用戶的選擇調用一個回調句柄 —— 換句話說,對于作者和出版者列表,只有一個 listHandler 回調句柄。
顯示書名列表需要使用 titlesHandler 。其余的功能仍然一樣:servlet 處理請求,用 XML 格式寫回響應。然后讀取、解析、格式化響應,用 HTML 改寫。可以用 HTML 把列表呈現為 select......options 標記。這個示例代碼段顯示了 titlesHandler 方法。
var temp = "<select name=\"titles\" multiple\>";
for (var i=0; i<index; i++) {
var listObj = req.responseXML.getElementsByTagName("list")[i];
temp = temp + "<option value=" + i +">" + listObj.childNodes[0].nodeValue
+ "</option>";
}
temp = temp + "</select>";
titles.innerHTML = temp;
|
迄今為止,我們演示了如何實現實時驗證和刷新。使用 AJAX,可以選擇多種方式給 Web 站點的用戶交互添加特色。下面運行應用程序。
運行和測試應用程序
運行應用程序
請下載示例代碼 wa-ajax-Library.war 并把它拷貝到 Tomcat 的 Webapp 目錄(例如,c:\Tomcat 5.0\Webapps)。要啟動 Tomcat 服務器,請輸入以下命令:
cd bin
C:\Tomcat 5.0\bin> catalina.bat start
|
Tomcat 現在啟動了,AJAX Web 應用程序也部署在其中。
測試應用程序
要測試應用程序:
- 請打開 Web 瀏覽器,指向
http://localhost:tomcatport/Library/order.jsp ,其中變量 tompcatport 是 Tomcat 服務器運行所在的端口。
將看到訂購屏幕。
- 在 Enter Subscription ID 字段中,輸入用戶 ID(“John” 除外)并離開字段。
向服務器異步發出的訂購 ID 請求會被驗證。可以看到 “Subscription not valid” 消息,如 圖 2 所示: 圖 2. “Subcription not valid” 屏幕

應用程序在不刷新瀏覽器的情況下,異步地驗證用戶并提供運行時驗證。
- 輸入用戶 ID 值 John。
將看到消息 “Subscription is valid”。訂購有效之后,應用程序就激活 Order 按鈕。
- 請選擇 By Author 或 By Publisher 按鈕分別填充作者或出版者下拉列表。
- 從下拉列表選擇一個作者或出版者。
書名區域被動態填充(如 圖 3 所示)。 圖 3. “Subcription is valid”屏幕

在選擇作者或出版者時,應用程序請求服務器在運行時提供來自服務器的與選中作者或出版者對應的書名信息。在不刷新瀏覽器的情況下顯示書名信息。
現在成功安裝和測試了示例 AJAX 應用程序。
結束語
結束語
AJAX 從開始至今已經走了很長的路。我們相信 AJAX 不僅僅可以用作設計模式,不過 AJAX 仍然有一些問題:
- 瀏覽器對
XMLHttpRequest 對象的支持可能是個限制。大多數瀏覽器都支持 XMLHttpRequest 對象,但是有少數不支持(通常是較老版本的瀏覽器)。
- AJAX 最適合顯示少量數據。如果要處理大量數據并在列表中實時顯示,那么 AJAX 可能不是合適的解決方案。
- AJAX 非常依賴于 JavaScript。如果瀏覽器不支持 JavaScript 或者用戶禁止了腳本選項,那么根本就不能利用 AJAX。
- AJAX 異步的性質不會保證多個請求的同步請求處理。如果需要對驗證或刷新進行優先處理,那么要相應地設計應用程序。
即使有這些可能的問題,AJAX 仍然是提高 Web 頁面和解決頁面重載問題的最佳解決方案。
Naveen Balani, 技術架構師, Webify Solutions Rajeev Hathi, 高級系統分析員, Satyam 計算機有限公司
|