Ajax漫步(四)處理服務器響應

處理服務器響應

發送請求,用戶高興地使用 Web 表單(同時服務器在處理請求),而現在服務器完成了請求處理。服務器查看 onreadystatechange 屬性確定要調用的方法。除此以外,可以將您的應用程序看作其他應用程序一樣,無論是否異步。換句話說,不一定要採取特殊的動作編寫響應服務器的方法,只需要改變表單,讓用戶訪問另一個 URL 或者做響應服務器需要的任何事情。這一節我們重點討論對服務器的響應和一種典型的動作 —— 即時改變用戶看到的表單中的一部分。

回調和 Ajax

現在我們已經看到如何告訴服務器完成後應該做什麼:將 XMLHttpRequest 對象的 onreadystatechange 屬性設置爲要運行的函數名。這樣,當服務器處理完請求後就會自動調用該函數。也不需要擔心該函數的任何參數。我們從一個簡單的方法開始,如 清單 12 所示。



清單 12. 回調方法的代碼
<script language="javascript" type="text/javascript">
   var request = false;
   try {
     request = new XMLHttpRequest();
   } catch (trymicrosoft) {
     try {
       request = new ActiveXObject("Msxml2.XMLHTTP");
     } catch (othermicrosoft) {
       try {
         request = new ActiveXObject("Microsoft.XMLHTTP");
       } catch (failed) {
         request = false;
       } 
     }
   }
   if (!request)
     alert("Error initializing XMLHttpRequest!");
   function getCustomerInfo() {
     var phone = document.getElementById("phone").value;
     var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
     request.open("GET", url, true);
     request.onreadystatechange = updatePage;
     request.send(null);
   }
   function updatePage() {
     alert("Server is done!");
   }
</script>

它僅僅發出一些簡單的警告,告訴您服務器什麼時候完成了任務。在自己的網頁中試驗這些代碼,然後在瀏覽器中打開(如果希望查看該例中的 XHTML,請參閱 清單 8)。輸入電話號碼然後離開該字段,將看到一個彈出的警告窗口(如 圖 3 所示),但是點擊 OK 又出現了……



圖 3. 彈出警告的 Ajax 代碼
彈出警告的 Ajax 代碼

根據瀏覽器的不同,在表單停止彈出警告之前會看到兩次、三次甚至四次警告。這是怎麼回事呢?原來我們還沒有考慮 HTTP 就緒狀態,這是請求/響應循環中的一個重要部分。

HTTP 就緒狀態

前面提到,服務器在完成請求之後會在 XMLHttpRequestonreadystatechange 屬性中查找要調用的方法。這是真的,但還不完整。事實上,每當 HTTP 就緒狀態改變時它都會調用該方法。這意味着什麼呢?首先必須理解 HTTP 就緒狀態。

HTTP 就緒狀態表示請求的狀態或情形。它用於確定該請求是否已經開始、是否得到了響應或者請求/響應模型是否已經完成。它還可以幫助確定讀取服務器提供的響應文本或數據是否安全。在 Ajax 應用程序中需要了解五種就緒狀態:

  • 0:請求沒有發出(在調用 open() 之前)。
  • 1:請求已經建立但還沒有發出(調用 send() 之前)。
  • 2:請求已經發出正在處理之中(這裏通常可以從響應得到內容頭部)。
  • 3:請求已經處理,響應中通常有部分數據可用,但是服務器還沒有完成響應。
  • 4:響應已完成,可以訪問服務器響應並使用它。

與大多數跨瀏覽器問題一樣,這些就緒狀態的使用也不盡一致。您也許期望任務就緒狀態從 0 到 1、2、3 再到 4,但實際上很少是這種情況。一些瀏覽器從不報告 0 或 1 而直接從 2 開始,然後是 3 和 4。其他瀏覽器則報告所有的狀態。還有一些則多次報告就緒狀態 1。在上一節中看到,服務器多次調用 updatePage(),每次調用都會彈出警告框 —— 可能和預期的不同!

對於 Ajax 編程,需要直接處理的惟一狀態就是就緒狀態 4,它表示服務器響應已經完成,可以安全地使用響應數據了。基於此,回調方法中的第一行應該如 清單 13 所示。



清單 13. 檢查就緒狀態
   function updatePage() {
     if (request.readyState == 4)
       alert("Server is done!");
   }

修改後就可以保證服務器的處理已經完成。嘗試運行新版本的 Ajax 代碼,現在就會看到與預期的一樣,只顯示一次警告信息了。

HTTP 狀態碼

雖然 清單 13 中的代碼看起來似乎不錯,但是還有一個問題 —— 如果服務器響應請求並完成了處理但是報告了一個錯誤怎麼辦?要知道,服務器端代碼應該明白它是由 Ajax、JSP、普通 HTML 表單或其他類型的代碼調用的,但只能使用傳統的 Web 專用方法報告信息。而在 Web 世界中,HTTP 代碼可以處理請求中可能發生的各種問題。

比方說,您肯定遇到過輸入了錯誤的 URL 請求而得到 404 錯誤碼的情形,它表示該頁面不存在。這僅僅是 HTTP 請求能夠收到的衆多錯誤碼中的一種(完整的狀態碼列表請參閱 參考資料 中的鏈接)。表示所訪問數據受到保護或者禁止訪問的 403 和 401 也很常見。無論哪種情況,這些錯誤碼都是從完成的響應 得到的。換句話說,服務器履行了請求(即 HTTP 就緒狀態是 4)但是沒有返回客戶機預期的數據。

因此除了就緒狀態外,還需要檢查 HTTP 狀態。我們期望的狀態碼是 200,它表示一切順利。如果就緒狀態是 4 而且狀態碼是 200,就可以處理服務器的數據了,而且這些數據應該就是要求的數據(而不是錯誤或者其他有問題的信息)。因此還要在回調方法中增加狀態檢查,如 清單 14 所示。



清單 14. 檢查 HTTP 狀態碼
 function updatePage() {
     if (request.readyState == 4)
       if (request.status == 200)
         alert("Server is done!");
   }

爲了增加更健壯的錯誤處理並儘量避免過於複雜,可以增加一兩個狀態碼檢查,請看一看 清單 15 中修改後的 updatePage() 版本。



清單 15. 增加一點錯誤檢查
   function updatePage() {
     if (request.readyState == 4)
       if (request.status == 200)
         alert("Server is done!");
       else if (request.status == 404)
         alert("Request URL does not exist");
       else
         alert("Error: status code is " + request.status);
   }

現在將 getCustomerInfo() 中的 URL 改爲不存在的 URL 看看會發生什麼。應該會看到警告信息說明要求的 URL 不存在 —— 好極了!很難處理所有的錯誤條件,但是這一小小的改變能夠涵蓋典型 Web 應用程序中 80% 的問題。

讀取響應文本

現在可以確保請求已經處理完成(通過就緒狀態),服務器給出了正常的響應(通過狀態碼),最後我們可以處理服務器返回的數據了。返回的數據保存在 XMLHttpRequest 對象的 responseText 屬性中。

關於 responseText 中的文本內容,比如格式和長度,有意保持含糊。這樣服務器就可以將文本設置成任何內容。比方說,一種腳本可能返回逗號分隔的值,另一種則使用管道符(即 | 字符)分隔的值,還有一種則返回長文本字符串。何去何從由服務器決定。

在本文使用的例子中,服務器返回客戶的上一個訂單和客戶地址,中間用管道符分開。然後使用訂單和地址設置表單中的元素值,清單 16 給出了更新顯示內容的代碼。



清單 16. 處理服務器響應
   function updatePage() {
     if (request.readyState == 4) {
       if (request.status == 200) {
         var response = request.responseText.split("|");
         document.getElementById("order").value = response[0];
         document.getElementById("address").innerHTML =
           response[1].replace(//n/g, "");
       } else
         alert("status is " + request.status);
     }
   }

首先,得到 responseText 並使用 JavaScript split() 方法從管道符分開。得到的數組放到 response 中。數組中的第一個值 —— 上一個訂單 —— 用 response[0] 訪問,被設置爲 ID 爲 “order” 的字段的值。第二個值 response[1],即客戶地址,則需要更多一點處理。因爲地址中的行用一般的行分隔符(“/n”字符)分隔,代碼中需要用 XHTML 風格的行分隔符 <br /> 來代替。替換過程使用 replace() 函數和正則表達式完成。最後,修改後的文本作爲 HTML 表單 div 中的內部 HTML。結果就是表單突然用客戶信息更新了,如圖 4 所示。



圖 4. 收到客戶數據後的 Break Neck 表單
收到客戶數據後的 Break Neck 表單

結束本文之前,我還要介紹 XMLHttpRequest 的另一個重要屬性 responseXML。如果服務器選擇使用 XML 響應則該屬性包含(也許您已經猜到)XML 響應。處理 XML 響應和處理普通文本有很大不同,涉及到解析、文檔對象模型(DOM)和其他一些問題。後面的文章中將進一步介紹 XML。但是因爲 responseXML 通常和 responseText 一起討論,這裏有必要提一提。對於很多簡單的 Ajax 應用程序 responseText 就夠了,但是您很快就會看到通過 Ajax 應用程序也能很好地處理 XML。





回頁首


結束語

您可能對 XMLHttpRequest 感到有點厭倦了,我很少看到一整篇文章討論一個對象,特別是這種簡單的對象。但是您將在使用 Ajax 編寫的每個頁面和應用程序中反覆使用該對象。坦白地說,關於 XMLHttpRequest 還真有一些可說的內容。下一期文章中將介紹如何在請求中使用 POSTGET,來設置請求中的內容頭部和從服務器響應讀取內容頭部,理解如何在請求/響應模型中編碼請求和處理 XML。

再往後我們將介紹常見 Ajax 工具箱。這些工具箱實際上隱藏了本文所述的很多細節,使得 Ajax 編程更容易。您也許會想,既然有這麼多工具箱爲何還要對底層的細節編碼。答案是,如果不知道應用程序在做什麼,就很難發現應用程序中的問題。

因此不要忽略這些細節或者簡單地瀏覽一下,如果便捷華麗的工具箱出現了錯誤,您就不必撓頭或者發送郵件請求支持了。如果瞭解如何直接使用 XMLHttpRequest,就會發現很容易調試和解決最奇怪的問題。只有讓其解決您的問題,工具箱纔是好東西。

因此請熟悉 XMLHttpRequest 吧。事實上,如果您有使用工具箱的 Ajax 代碼,可以嘗試使用 XMLHttpRequest 對象及其屬性和方法重新改寫。這是一種不錯的練習,可以幫助您更好地理解其中的原理。

下一期文章中將進一步討論該對象,探討它的一些更有趣的屬性(如 responseXML),以及如何使用 POST 請求和以不同的格式發送數據。請開始編寫代碼吧,一個月後我們再繼續討論。

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章