| 導購 | 订阅 | 在线投稿
分享
 
 
 

[四天學會ajax] 學習Ajax教程第三天,Ajax 中的高級請求和響應

2008-10-02 07:47:49  編輯來源:互聯網  简体版  手機版  評論  字體: ||
 
  對于很多 Web 開發人員來說,只需要生成簡單的請求並接收簡單的響應即可;但是對于希望掌握 Ajax 的開發人員來說,必須要全面理解 HTTP 狀態代碼、就緒狀態和 XMLHttpRequest 對象。在本文中,Brett McLaughlin 將向您介紹各種狀態代碼,並展示浏覽器如何對其進行處理,本文還給出了在 Ajax 中使用的比較少見的 HTTP 請求。

  在本系列的 上篇文章 中,我們將詳細介紹 XMLHttpRequest 對象,它是 Ajax 應用程序的中心,負責處理服務器端應用程序和腳本的請求,並處理從服務器端組件返回的數據。由于所有的 Ajax 應用程序都要使用 XMLHttpRequest 對象,因此您可能會希望熟悉這個對象,從而能夠讓 Ajax 執行得更好。

  在本文中,我將在上一篇文章的基礎上重點介紹這個請求對象的 3 個關鍵部分的內容:

  ·HTTP 就緒狀態

  ·HTTP 狀態代碼

  ·可以生成的請求類型

  這三部分內容都是在構造一個請求時所要考慮的因素;但是介紹這些主題的內容太少了。然而,如果您不僅僅是想了解 Ajax 編程的常識,而是希望了解更多內容,就需要熟悉就緒狀態、狀態代碼和請求本身的內容。當應用程序出現問題時 —— 這種問題總是存在 —— 那麽如果能夠正確理解就緒狀態、如何生成一個 HEAD 請求或者 400 的狀態代碼的確切含義,就可以在 5 分鍾內調試出問題,而不是在各種挫折和困惑中度過 5 個小時。

  下面讓我們首先來看一下 HTTP 就緒狀態。

  深入了解 HTTP 就緒狀態

  您應該還記得在上一篇文章中 XMLHttpRequest 對象有一個名爲 readyState 的屬性。這個屬性確保服務器已經完成了一個請求,通常會使用一個回調函數從服務器中讀出數據來更新 Web 表單或頁面的內容。清單 1 給出了一個簡單的例子(這也是本系列的上一篇文章中的一個例子 —— 請參見 參考資料)。

  XMLHttpRequest 或 XMLHttp:換名玫瑰

  Microsoft™ 和 Internet Explorer 使用了一個名爲 XMLHttp 的對象,而不是 XMLHttpRequest 對象,而 Mozilla、Opera、Safari 和 大部分非 Microsoft 浏覽器都使用的是後者。爲了簡單性起見,我將這兩個對象都簡單地稱爲 XMLHttpRequest。這既符合我們在 Web 上看到的情況,又符合 Microsoft 在 Internet Explorer 7.0 中使用 XMLHttpRequest 作爲請求對象的意圖。(有關這個問題的更多內容,請參見 第 2 部分。)

  清單 1. 在回調函數中處理服務器的響應

  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, "<br />");

   } else

   alert("status is " + request.status);

   }

  }

  這顯然是就緒狀態最常見(也是最簡單)的用法。正如您從數字 "4" 中可以看出的一樣,還有其他幾個就緒狀態(您在上一篇文章中也看到過這個清單 —— 請參見 參考資料):

  ·0:請求未初始化(還沒有調用 open())。

  ·1:請求已經建立,但是還沒有發送(還沒有調用 send())。

  ·2:請求已發送,正在處理中(通常現在可以從響應中獲取內容頭)。

  ·3:請求在處理中;通常響應中已有部分數據可用了,但是服務器還沒有完成響應的生成。

  ·4:響應已完成;您可以獲取並使用服務器的響應了。

  如果您希望不僅僅是了解 Ajax 編程的基本知識,那麽就不但需要知道這些狀態,了解這些狀態是何時出現的,以及如何來使用這些狀態。首先,您需要學習在每種就緒狀態下可能碰到的是哪種請求狀態。不幸的是,這一點並不直觀,而且會涉及幾種特殊的情況。

  隱秘就緒狀態

  第一種就緒狀態的特點是 readyState 屬性爲 0(readyState == 0),表示未初始化狀態。一旦對請求對象調用 open() 之後,這個屬性就被設置爲 1。由于您通常都是在一對請求進行初始化之後就立即調用 open(),因此很少會看到 readyState == 0 的狀態。另外,未初始化的就緒狀態在實際的應用程序中是沒有真正的用處的。

  不過爲了滿足我們的興趣,請參見 清單 2 的內容,其中顯示了如何在 readyState 被設置爲 0 時來獲取這種就緒狀態。

  清單 2. 獲取 0 就緒狀態

   function getSalesData() {

   // Create a request object

   createRequest();

   alert("Ready state is: " + request.readyState);

   // Setup (initialize) the request

   var url = "/boards/servlet/UpdateBoardSales";

   request.open("GET", url, true);

   request.onreadystatechange = updatePage;

   request.send(null);

   }

  在這個簡單的例子中,getSalesData() 是 Web 頁面調用來啓動請求(例如點擊一個按鈕時)所使用的函數。注意您必須在調用 open()之前 來查看就緒狀態。圖 1 給出了運行這個應用程序的結果。

  圖 1. 就緒狀態 0

  顯然,這並不能爲您帶來多少好處;需要確保 尚未 調用 open() 函數的情況很少。在大部分 Ajax 編程的真實情況中,這種就緒狀態的唯一用法就是使用相同的 XMLHttpRequest 對象在多個函數之間生成多個請求。在這種(不常見的)情況中,您可能會在生成新請求之前希望確保請求對象是處于未初始化狀態(readyState == 0)。這實際上是要確保另外一個函數沒有同時使用這個對象。

  查看正在處理的請求的就緒狀態

  除了 0 就緒狀態之外,請求對象還需要依次經曆典型的請求和響應的其他幾種就緒狀態,最後才以就緒狀態 4 的形式結束。這就是爲什麽您在大部分回調函數中都可以看到 if (request.readyState == 4) 這行代碼;它確保服務器已經完成對請求的處理,現在可以安全地更新 Web 頁面或根據從服務器返回來的數據來進行操作了。

  要查看這種狀態發生的過程非常簡單。如果就緒狀態爲 4,我們不僅要運行回調函數中的代碼,而且還要在每次調用回調函數時都輸出就緒狀態。 清單 3 給出了一個實現這種功能的例子。

  當 0 等于 4 時

  在多個 JavaScript 函數都使用相同的請求對象時,您需要檢查就緒狀態 0 來確保這個請求對象沒有正在使用,這種機制會産生問題。由于 readyState == 4 表示一個已完成的請求,因此您經常會發現那些目前沒在使用的處于就緒狀態的請求對象仍然被設置成了 4 —— 這是因爲從服務器返回來的數據已經使用過了,但是從它們被設置爲就緒狀態之後就沒有進行任何變化。有一個函數 abort() 會重新設置請求對象,但是這個函數卻不是真正爲了這個目的而使用的。如果您 必須 使用多個函數,最好是爲每個函數都創建並使用一個函數,而不是在多個函數之間共享相同的對象。

  清單 3. 查看就緒狀態

   function updatePage() {

   // Output the current ready state

   alert("updatePage() called with ready state of " + request.readyState);

   }

  如果您不確定如何運行這個函數,就需要創建一個函數,然後在 Web 頁面中調用這個函數,並讓它向服務器端的組件發送一個請求(例如 清單 2 給出的函數,或本系列文章的第 1 部分和第 2 部分中給出的例子)。確保在建立請求時,將回調函數設置爲 updatePage();要實現這種設置,可以將請求對象的 onreadystatechange 屬性設置爲 updatePage()。

  這段代碼就是 onreadystatechange 意義的一個確切展示 —— 每次請求的就緒狀態發生變化時,就調用 updatePage(),然後我們就可以看到一個警告了。圖 2 給出了一個調用這個函數的例子,其中就緒狀態爲 1。

  圖 2. 就緒狀態 1

  您可以自己嘗試運行這段代碼。將其放入 Web 頁面中,然後激活事件處理程序(單擊按鈕,在域之間按 tab 鍵切換焦點,或者使用設置的任何方法來觸發請求)。這個回調函數會運行多次 —— 每次就緒狀態都會改變 —— 您可以看到每個就緒狀態的警告。這是跟蹤請求所經曆的各個階段的最好方法。

  浏覽器的不一致性

  在對這個過程有一個基本的了解之後,請試著從幾個不同的浏覽器中訪問您的頁面。您應該會注意到各個浏覽器如何處理這些就緒狀態並不一致。例如,在 Firefox 1.5 中,您會看到以下就緒狀態:

  ·1

  ·2

  ·3

  ·4

  這並不奇怪,因爲每個請求狀態都在這裏表示出來了。然而,如果您使用 Safari 來訪問相同的應用程序,就應該看到 —— 或者看不到 —— 一些有趣的事情。下面是在 Safari 2.0.1 中看到的狀態:

  ·2

  ·3

  ·4

  Safari 實際上把第一個就緒狀態給丟棄了,也並沒有什麽明顯的原因說明爲什麽要這樣做;不過這就是 Safari 的工作方式。這還說明了一個重要的問題:盡管在使用服務器上的數據之前確保請求的狀態爲 4 是一個好主意,但是依賴于每個過渡期就緒狀態編寫的代碼的確會在不同的浏覽器上得到不同的結果。

  例如,在使用 Opera 8.5 時,所顯示的就緒狀態情況就更加糟糕了:

  ·3

  ·4

  最後,Internet Explorer 會顯示如下狀態:

  ·1

  ·2

  ·3

  ·4

  如果您碰到請求方面的問題,這就是用來發現問題的 首要之處。最好的方式是在 Internet Explorer 和 Firefox 都進行一下測試 —— 您會看到所有這 4 種狀態,並可以檢查請求的每個狀態所處的情況。

  接下來我們再來看一下響應端的情況。

  顯微鏡下的響應數據

  一旦我們理解在請求過程中發生的各個就緒狀態之後,接下來就可以來看一下 XMLHttpRequest 對象的另外一個方面了 —— responseText 屬性。回想一下在上一篇文章中我們介紹過的內容,就可以知道這個屬性用來從服務器上獲取數據。一旦服務器完成對請求的處理之後,就可以將響應請求數據所需要的任何數據放到請求的 responseText 中了。然後回調函數就可以使用這些數據,如 清單 1 和 清單 4 所示。

  清單 4. 使用服務器上返回的響應

   function updatePage() {

   if (request.readyState == 4) {

   var newTotal = request.responseText;

   var totalSoldEl = document.getElementById("total-sold");

   var netProfitEl = document.getElementById("net-profit");

   replaceText(totalSoldEl, newTotal);

   /* 圖 out the new net profit */

   var boardCostEl = document.getElementById("board-cost");

   var boardCost = getText(boardCostEl);

   var manCostEl = document.getElementById("man-cost");

   var manCost = getText(manCostEl);

   var profitPerBoard = boardCost - manCost;

   var netProfit = profitPerBoard * newTotal;

   /* Update the net profit on the sales form */

   netProfit = Math.round(netProfit * 100) / 100;

   replaceText(netProfitEl, netProfit);

   }

  清單 1 相當簡單;清單 4 稍微有點複雜,但是它們在開始時都要檢查就緒狀態,並獲取 responseText 屬性的值。

  查看請求的響應文本

  與就緒狀態類似,responseText 屬性的值在整個請求的生命周期中也會發生變化。要查看這種變化,請使用如 清單 5 所示的代碼來測試請求的響應文本,以及它們的就緒狀態。

  清單 5. 測試 responseText 屬性

   function updatePage() {

   // Output the current ready state

   alert("updatePage() called with ready state of " + request.readyState +

   " and a response text of '" + request.responseText + "'");

   }

  現在在浏覽器中打開 Web 應用程序,並激活您的請求。要更好地看到這段代碼的效果,請使用 Firefox 或 Internet Explorer,因爲這兩個浏覽器都可以報告出請求過程中所有可能的就緒狀態。例如在就緒狀態 2 中,就沒有定義 responseText (請參見 圖 3);如果 JavaScript 控制台也已經打開了,您就會看到一個錯誤。

  圖 3. 就緒狀態爲 2 的響應文本

  不過在就緒狀態 3 中,服務器已經在 responseText 屬性中放上了一個值,至少在這個例子中是這樣(請參見 圖 4)。

  圖 4. 就緒狀態爲 3 的響應文本

  您會看到就緒狀態爲 3 的響應在每個腳本、每個服務器甚至每個浏覽器上都是不一樣的。不過,這在調試應用程序中依然是非常有用的。

  獲取安全數據

  所有的文檔和規範都強調,只有在就緒狀態爲 4 時數據才可以安全使用。相信我,當就緒狀態爲 3 時,您很少能找到無法從 responseText 屬性獲取數據的情況。然而,在應用程序中將自己的邏輯依賴于就緒狀態 3 可不是什麽好主意 —— 一旦您編寫了依賴于就緒狀態 3 的完整數據的的代碼,幾乎就要自己來負責當時的數據不完整問題了。

  比較好的做法是向用戶提供一些反饋,說明在處于就緒狀態 3 時,很快就會有響應了。盡管使用 alert() 之類的函數顯然不是什麽好主意 —— 使用 Ajax 然後使用一個警告對話框來阻塞用戶顯然是錯誤的 —— 不過您可以在就緒狀態發生變化時更新表單或頁面中的域。例如,對于就緒狀態 1 來說要將進度指示器的寬度設置爲 25%,對于就緒狀態 2 來說要將進度指示器的寬度設置爲 50%,對于就緒狀態 3 來說要將進度指示器的寬度設置爲 75%,當就緒狀態爲 4 時將進度指示器的寬度設置爲 100%(完成)。

  當然,正如您已經看到的一樣,這種方法非常聰明,但它是依賴于浏覽器的。在 Opera 上,您永遠都不會看到前兩個就緒狀態,而在 Safari 上則沒有第一個(1)。由于這個原因,我將這段代碼留作練習,而沒有在本文中包括進來。

  現在應該來看一下狀態代碼了。

  深入了解 HTTP 狀態代碼

  有了就緒狀態和您在 Ajax 編程技術中學習到的服務器的響應,您就可以爲 Ajax 應用程序添加另外一級複雜性了 —— 這要使用 HTTP 狀態代碼。這些代碼對于 Ajax 來說並沒有什麽新鮮。從 Web 出現以來,它們就已經存在了。在 Web 浏覽器中您可能已經看到過幾個狀態代碼:

  ·401:未經授權

  ·403:禁止

  ·404:沒找到

  您可以找到更多的狀態代碼(完整清單請參見 參考資料)。要爲 Ajax 應用程序另外添加一層控制和響應(以及更爲健壯的錯誤處理)機制,您需要適當地查看請求和響應中的狀態代碼。

  200:一切正常

  在很多 Ajax 應用程序中,您將看到一個回調函數,它負責檢查就緒狀態,然後繼續利用從服務器響應中返回的數據,如 清單 6 所示。

  清單 6. 忽略狀態代碼的回調函數

   function updatePage() {

   if (request.readyState == 4) {

   var response = request.responseText.split("|");

   document.getElementById("order").value = response[0];

   document.getElementById("address").innerHTML =

   response[1].replace(/\n/g, "<br />");

   }

   }

  這對于 Ajax 編程來說證明是一種短視而錯誤的方法。如果腳本需要認證,而請求卻沒有提供有效的證書,那麽服務器就會返回諸如 403 或 401 之類的錯誤代碼。然而,由于服務器對請求進行了應答,因此就緒狀態就被設置爲 4(即使應答並不是請求所期望的也是如此)。最終,用戶沒有獲得有效數據,當 JavaScript 試圖使用不存在的服務器數據時就可能會出現嚴重的錯誤。

  它花費了最小的努力來確保服務器不但完成了一個請求,而且還返回了一個 「一切良好」 的狀態代碼。這個代碼是 "200",它是通過 XMLHttpRequest 對象的 status 屬性來報告的。爲了確保服務器不但完成了一個請求,而且還報告了一個 OK 狀態,請在您的回調函數中添加另外一個檢查功能,如 清單 7 所示。

  清單 7. 檢查有效狀態代碼

   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, "<br />");

   } else

   alert("status is " + request.status);

   }

   }

  通過添加這幾行代碼,您就可以確認是否存在問題,用戶會看到一個有用的錯誤消息,而不僅僅是看到一個由斷章取義的數據所構成的頁面,而沒有任何解釋。

  重定向和重新路由

  在深入介紹有關錯誤的內容之前,我們有必要來討論一下有關一個在使用 Ajax 時 並不需要 關心的問題 —— 重定向。在 HTTP 狀態代碼中,這是 300 系列的狀態代碼,包括:

  ·301:永久移動

  ·302:找到(請求被重新定向到另外一個 URL/URI 上)

  ·305:使用代理(請求必須使用一個代理來訪問所請求的資源)

  Ajax 程序員可能並不太關心有關重定向的問題,這是由于兩方面的原因:

  ·首先,Ajax 應用程序通常都是爲一個特定的服務器端腳本、servlet 或應用程序而編寫的。對于那些您看不到就消失了的組件來說,Ajax 程序員就不太清楚了。因此有時您會知道資源已經移動了(因爲您移動了它,或者通過某種手段移動了它),接下來要修改請求中的 URL,並且不會再碰到這種結果了。

  更爲重要的一個原因是:Ajax 應用程序和請求都是封裝在沙盒中的。這就意味著提供生成 Ajax 請求的 Web 頁面的域必須是對這些請求進行響應的域。因此 ebay.com 所提供的 Web 頁面就不能對一個在 amazon.com 上運行的腳本生成一個 Ajax 風格的請求;在 ibm.com 上的 Ajax 應用程序也無法對在 netbeans.org 上運行的 servlets 發出請求。

  ·結果是您的請求無法重定向到其他服務器上,而不會産生安全性錯誤。在這些情況中,您根本就不會得到狀態代碼。通常在調試控制台中都會産生一個 JavaScript 錯誤。因此,在對狀態代碼進行充分的考慮之後,您就可以完全忽略重定向代碼的問題了。

  結果是您的請求無法重定向到其他服務器上,而不會産生安全性錯誤。在這些情況中,您根本就不會得到狀態代碼。通常在調試控制台中都會産生一個 JavaScript 錯誤。因此,在對狀態代碼進行充分的考慮之後,您就可以完全忽略重定向代碼的問題了。

  錯誤

  一旦接收到狀態代碼 200 並且意識到可以很大程度上忽略 300 系列的狀態代碼之後,所需要擔心的唯一一組代碼就是 400 系列的代碼了,這說明了不同類型的錯誤。回頭再來看一下 清單 7,並注意在對錯誤進行處理時,只將少數常見的錯誤消息輸出給用戶了。盡管這是朝正確方向前進的一步,但是要告訴從事應用程序開發的用戶和程序員究竟發生了什麽問題,這些消息仍然是沒有太大用處的。

  首先,我們要添加對找不到的頁的支持。實際上這在大部分産品系統中都不應該出現,但是在測試腳本位置發生變化或程序員輸入了錯誤的 URL 時,這種情況並不罕見。如果您可以自然地報告 404 錯誤,就可以爲那些困擾不堪的用戶和程序員提供更多幫助。例如,如果服務器上的一個腳本被刪除了,我們就可以使用 清單 7 中的代碼,這樣用戶就會看到一個如 圖 5 所示的非描述性錯誤。

  邊界情況和困難情況

  看到現在,一些新手程序員就可能會這究竟是要討論什麽內容。有一點事實大家需要知道:只有不到 5% 的 Ajax 請求需要使用諸如 2、3 之類的就緒狀態和諸如 403 之類的狀態代碼(實際上,這個比率可能更接近于 1% 甚至更少)。這些情況非常重要,稱爲 邊界情況(edge case) —— 它們只會在一些非常特殊的情況下發生,其中遇到的都是最奇特的問題。雖然這些情況並不普遍,但是這些邊界情況卻占據了大部分用戶所碰到的問題的 80%!

  對于典型的用戶來說,應用程序 100 次都是正常工作的這個事實通常都會被忘記,然而應用程序只要一次出錯就會被他們清楚地記住。如果您可以很好地處理邊界情況(或困難情況),就可以爲再次訪問站點的用戶提供滿意的回報。

  圖 5. 常見錯誤處理

  用戶無法判斷問題究竟是認證問題、沒找到腳本(此處就是這種情況)、用戶錯誤還是代碼中有些地方産生了問題。添加一些簡單的代碼可以讓這個錯誤更加具體。請參照 清單 8,它負責處理沒找到的腳本或認證發生錯誤的情況,在出現這些錯誤時都會給出具體的消息。

  清單 8. 檢查有效狀態代碼

   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, "<br />");

   } else if (request.status == 404) {

   alert ("Requested URL is not found.");

   } else if (request.status == 403) {

   alert("Access denied.");

   } else

   alert("status is " + request.status);

   }

   }

  雖然這依然相當簡單,但是它的確多提供了一些有用的信息。圖 6 給出了與 圖 5 相同的錯誤,但是這一次錯誤處理代碼向用戶或程序員更好地說明了究竟發生了什麽。

  圖 6. 特殊錯誤處理

  在我們自己的應用程序中,可以考慮在發生認證失敗的情況時清除用戶名和密碼,並向屏幕上添加一條錯誤消息。我們可以使用類似的方法來更好地處理找不到腳本或其他 400 類型的錯誤(例如 405 表示不允許使用諸如發送 HEAD 請求之類不可接受的請求方法,而 407 則表示需要進行代理認證)。然而不管采用哪種選擇,都需要從對服務器上返回的狀態代碼開始入手進行處理。

  其他請求類型

  如果您真希望控制 XMLHttpRequest 對象,可以考慮最後實現這種功能 —— 將 HEAD 請求添加到指令中。在前兩篇文章中,我們已經介紹了如何生成 GET 請求;在馬上就要發表的一篇文章中,您會學習有關使用 POST 請求將數據發送到服務器上的知識。不過本著增強錯誤處理和信息搜集的精神,您應該學習如何生成 HEAD 請求。

  生成請求

  實際上生成 HEAD 請求非常簡單;您可以使用 "HEAD"(而不是 "GET" 或 "POST")作爲第一個參數來調用 open() 方法,如 清單 9 所示。

  清單 9. 使用 Ajax 生成一個 HEAD 請求

   function getSalesData() {

   createRequest();

   var url = "/boards/servlet/UpdateBoardSales";

   request.open("HEAD", url, true);

   request.onreadystatechange = updatePage;

   request.send(null);

   }

  當您這樣生成一個 HEAD 請求時,服務器並不會像對 GET 或 POST 請求一樣返回一個真正的響應。相反,服務器只會返回資源的 頭(header),這包括響應中內容最後修改的時間、請求資源是否存在和很多其他有用信息。您可以在服務器處理並返回資源之前使用這些信息來了解有關資源的信息。

  對于這種請求您可以做的最簡單的事情就是簡單地輸出所有的響應頭的內容。這可以讓您了解通過 HEAD 請求可以使用什麽。清單 10 提供了一個簡單的回調函數,用來輸出從 HEAD 請求中獲得的響應頭的內容。

  清單 10. 輸出從 HEAD 請求中獲得的響應頭的內容

   function updatePage() {

   if (request.readyState == 4) {

   alert(request.getAllResponseHeaders());

   }

   }

  請參見 圖 7,其中顯示了從一個向服務器發出的 HEAD 請求的簡單 Ajax 應用程序返回的響應頭。

  您可以單獨使用這些頭(從服務器類型到內容類型)在 Ajax 應用程序中提供其他信息或功能。

  檢查 URL

  您已經看到了當 URL 不存在時應該如何檢查 404 錯誤。如果這變成一個常見的問題 —— 可能是缺少了一個特定的腳本或 servlet —— 那麽您就可能會希望在生成完整的 GET 或 POST 請求之前來檢查這個 URL。要實現這種功能,生成一個 HEAD 請求,然後在回調函數中檢查 404 錯誤;清單 11 給出了一個簡單的回調函數。

  清單 11. 檢查某個 URL 是否存在

   function updatePage() {

   if (request.readyState == 4) {

   if (request.status == 200) {

   alert("URL exists");

   } else if (request.status == 404) {

   alert("URL does not exist.");

   } else {

   alert("Status is: " + request.status);

   }

   }

   }

  誠實地說,這段代碼的價值並不太大。服務器必須對請求進行響應,並構造一個響應來填充內容長度的響應頭,因此並不能節省任何處理時間。另外,這花費的時間與生成請求並使用 HEAD 請求來查看 URL 是否存在所需要的時間一樣多,因爲它要生成使用 GET 或 POST 的請求,而不僅僅是如 清單 7 所示一樣來處理錯誤代碼。不過,有時確切地了解目前什麽可用也是非常有用的;您永遠不會知道何時創造力就會迸發或者何時需要 HEAD 請求!

  有用的 HEAD 請求

  您會發現 HEAD 請求非常有用的一個領域是用來查看內容的長度或內容的類型。這樣可以確定是否需要發回大量數據來處理請求,和服務器是否試圖返回二進制數據,而不是 HTML、文本或 XML(在 JavaScript 中,這 3 種類型的數據都比二進制數據更容易處理)。

  在這些情況中,您只使用了適當的頭名,並將其傳遞給 XMLHttpRequest 對象的 getResponseHeader() 方法。因此要獲取響應的長度,只需要調用 request.getResponseHeader("Content-Length");。要獲取內容類型,請使用 request.getResponseHeader("Content-Type");。

  在很多應用程序中,生成 HEAD 請求並沒有增加任何功能,甚至可能會導致請求速度變慢(通過強制生成一個 HEAD 請求來獲取有關響應的數據,然後在使用一個 GET 或 POST 請求來真正獲取響應)。然而,在出現您不確定有關腳本或服務器端組件的情況時,使用 HEAD 請求可以獲取一些基本的數據,而不需要對響應數據真正進行處理,也不需要大量的帶寬來發送響應。

  結束語

  對于很多 Ajax 和 Web 程序員來說,本文中介紹的內容似乎是太高級了。生成 HEAD 請求的價值是什麽呢?到底在什麽情況下需要在 JavaScript 中顯式地處理重定向狀態代碼呢?這些都是很好的問題;對于簡單的應用程序來說,答案是這些高級技術的價值並不是非常大。

  然而,Web 已經不再是只需實現簡單應用程序的地方了;用戶已經變得更加高級,客戶期望能夠獲得更好的穩定性、更高級的錯誤報告,如果應用程序有 1% 的時間停機,那麽經理就可能會因此而被解雇。

  因此您的工作就不能僅僅局限于簡單的應用程序了,而是需要更深入理解 XMLHttpRequest。

  ·如果您可以考慮各種就緒狀態 —— 並且理解了這些就緒狀態在不同浏覽器之間的區別 —— 就可以快速調試應用程序了。您甚至可以基于就緒狀態而開發一些創造性的功能,並向用戶和客戶回報請求的狀態。

  ·如果您要對狀態代碼進行控制,就可以設置應用程序來處理腳本錯誤、非預期的響應以及邊緣情況。結果是應用程序在所有的時間都可以正常工作,而不僅僅是只能一切都正常的情況下才能運行。

  ·增加這種生成 HEAD 請求的能力,檢查某個 URL 是否存在,以及確認某個文件是否被修改過,這樣就可以確保用戶可以獲得有效的頁面,用戶所看到的信息都是最新的,(最重要的是)讓他們驚訝這個應用程序是如何健壯和通用。

  本文的目的並非是要讓您的應用程序顯得十分華麗,而是幫助您去掉黃色聚光燈後重點昭顯文字的美麗,或者外觀更像桌面一樣。盡管這些都是 Ajax 的功能(在後續幾篇文章中就會介紹),不過它們卻像是蛋糕表面的一層奶油。如果您可以使用 Ajax 來構建一個堅實的基礎,讓應用程序可以很好地處理錯誤和問題,用戶就會返回您的站點和應用程序。在接下來的文章中,我們將添加這種直觀的技巧,這會讓客戶興奮得發抖。(認真地說,您一定不希望錯過下一篇文章!)
 
對于很多 Web 開發人員來說,只需要生成簡單的請求並接收簡單的響應即可;但是對于希望掌握 Ajax 的開發人員來說,必須要全面理解 HTTP 狀態代碼、就緒狀態和 XMLHttpRequest 對象。在本文中,Brett McLaughlin 將向您介紹各種狀態代碼,並展示浏覽器如何對其進行處理,本文還給出了在 Ajax 中使用的比較少見的 HTTP 請求。 在本系列的 上篇文章 中,我們將詳細介紹 XMLHttpRequest 對象,它是 Ajax 應用程序的中心,負責處理服務器端應用程序和腳本的請求,並處理從服務器端組件返回的數據。由于所有的 Ajax 應用程序都要使用 XMLHttpRequest 對象,因此您可能會希望熟悉這個對象,從而能夠讓 Ajax 執行得更好。 在本文中,我將在上一篇文章的基礎上重點介紹這個請求對象的 3 個關鍵部分的內容: ·HTTP 就緒狀態 ·HTTP 狀態代碼 ·可以生成的請求類型 這三部分內容都是在構造一個請求時所要考慮的因素;但是介紹這些主題的內容太少了。然而,如果您不僅僅是想了解 Ajax 編程的常識,而是希望了解更多內容,就需要熟悉就緒狀態、狀態代碼和請求本身的內容。當應用程序出現問題時 —— 這種問題總是存在 —— 那麽如果能夠正確理解就緒狀態、如何生成一個 HEAD 請求或者 400 的狀態代碼的確切含義,就可以在 5 分鍾內調試出問題,而不是在各種挫折和困惑中度過 5 個小時。 下面讓我們首先來看一下 HTTP 就緒狀態。 深入了解 HTTP 就緒狀態 您應該還記得在上一篇文章中 XMLHttpRequest 對象有一個名爲 readyState 的屬性。這個屬性確保服務器已經完成了一個請求,通常會使用一個回調函數從服務器中讀出數據來更新 Web 表單或頁面的內容。清單 1 給出了一個簡單的例子(這也是本系列的上一篇文章中的一個例子 —— 請參見 參考資料)。 XMLHttpRequest 或 XMLHttp:換名玫瑰 Microsoft™ 和 Internet Explorer 使用了一個名爲 XMLHttp 的對象,而不是 XMLHttpRequest 對象,而 Mozilla、Opera、Safari 和 大部分非 Microsoft 浏覽器都使用的是後者。爲了簡單性起見,我將這兩個對象都簡單地稱爲 XMLHttpRequest。這既符合我們在 Web 上看到的情況,又符合 Microsoft 在 Internet Explorer 7.0 中使用 XMLHttpRequest 作爲請求對象的意圖。(有關這個問題的更多內容,請參見 第 2 部分。) 清單 1. 在回調函數中處理服務器的響應 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, "<br />"); } else alert("status is " + request.status); } } 這顯然是就緒狀態最常見(也是最簡單)的用法。正如您從數字 "4" 中可以看出的一樣,還有其他幾個就緒狀態(您在上一篇文章中也看到過這個清單 —— 請參見 參考資料): ·0:請求未初始化(還沒有調用 open())。 ·1:請求已經建立,但是還沒有發送(還沒有調用 send())。 ·2:請求已發送,正在處理中(通常現在可以從響應中獲取內容頭)。 ·3:請求在處理中;通常響應中已有部分數據可用了,但是服務器還沒有完成響應的生成。 ·4:響應已完成;您可以獲取並使用服務器的響應了。 如果您希望不僅僅是了解 Ajax 編程的基本知識,那麽就不但需要知道這些狀態,了解這些狀態是何時出現的,以及如何來使用這些狀態。首先,您需要學習在每種就緒狀態下可能碰到的是哪種請求狀態。不幸的是,這一點並不直觀,而且會涉及幾種特殊的情況。 隱秘就緒狀態 第一種就緒狀態的特點是 readyState 屬性爲 0(readyState == 0),表示未初始化狀態。一旦對請求對象調用 open() 之後,這個屬性就被設置爲 1。由于您通常都是在一對請求進行初始化之後就立即調用 open(),因此很少會看到 readyState == 0 的狀態。另外,未初始化的就緒狀態在實際的應用程序中是沒有真正的用處的。 不過爲了滿足我們的興趣,請參見 清單 2 的內容,其中顯示了如何在 readyState 被設置爲 0 時來獲取這種就緒狀態。 清單 2. 獲取 0 就緒狀態 function getSalesData() { // Create a request object createRequest(); alert("Ready state is: " + request.readyState); // Setup (initialize) the request var url = "/boards/servlet/UpdateBoardSales"; request.open("GET", url, true); request.onreadystatechange = updatePage; request.send(null); } 在這個簡單的例子中,getSalesData() 是 Web 頁面調用來啓動請求(例如點擊一個按鈕時)所使用的函數。注意您必須在調用 open()之前 來查看就緒狀態。圖 1 給出了運行這個應用程序的結果。 圖 1. 就緒狀態 0 顯然,這並不能爲您帶來多少好處;需要確保 尚未 調用 open() 函數的情況很少。在大部分 Ajax 編程的真實情況中,這種就緒狀態的唯一用法就是使用相同的 XMLHttpRequest 對象在多個函數之間生成多個請求。在這種(不常見的)情況中,您可能會在生成新請求之前希望確保請求對象是處于未初始化狀態(readyState == 0)。這實際上是要確保另外一個函數沒有同時使用這個對象。 查看正在處理的請求的就緒狀態 除了 0 就緒狀態之外,請求對象還需要依次經曆典型的請求和響應的其他幾種就緒狀態,最後才以就緒狀態 4 的形式結束。這就是爲什麽您在大部分回調函數中都可以看到 if (request.readyState == 4) 這行代碼;它確保服務器已經完成對請求的處理,現在可以安全地更新 Web 頁面或根據從服務器返回來的數據來進行操作了。 要查看這種狀態發生的過程非常簡單。如果就緒狀態爲 4,我們不僅要運行回調函數中的代碼,而且還要在每次調用回調函數時都輸出就緒狀態。 清單 3 給出了一個實現這種功能的例子。 當 0 等于 4 時 在多個 JavaScript 函數都使用相同的請求對象時,您需要檢查就緒狀態 0 來確保這個請求對象沒有正在使用,這種機制會産生問題。由于 readyState == 4 表示一個已完成的請求,因此您經常會發現那些目前沒在使用的處于就緒狀態的請求對象仍然被設置成了 4 —— 這是因爲從服務器返回來的數據已經使用過了,但是從它們被設置爲就緒狀態之後就沒有進行任何變化。有一個函數 abort() 會重新設置請求對象,但是這個函數卻不是真正爲了這個目的而使用的。如果您 必須 使用多個函數,最好是爲每個函數都創建並使用一個函數,而不是在多個函數之間共享相同的對象。 清單 3. 查看就緒狀態 function updatePage() { // Output the current ready state alert("updatePage() called with ready state of " + request.readyState); } 如果您不確定如何運行這個函數,就需要創建一個函數,然後在 Web 頁面中調用這個函數,並讓它向服務器端的組件發送一個請求(例如 清單 2 給出的函數,或本系列文章的第 1 部分和第 2 部分中給出的例子)。確保在建立請求時,將回調函數設置爲 updatePage();要實現這種設置,可以將請求對象的 onreadystatechange 屬性設置爲 updatePage()。 這段代碼就是 onreadystatechange 意義的一個確切展示 —— 每次請求的就緒狀態發生變化時,就調用 updatePage(),然後我們就可以看到一個警告了。圖 2 給出了一個調用這個函數的例子,其中就緒狀態爲 1。 圖 2. 就緒狀態 1 您可以自己嘗試運行這段代碼。將其放入 Web 頁面中,然後激活事件處理程序(單擊按鈕,在域之間按 tab 鍵切換焦點,或者使用設置的任何方法來觸發請求)。這個回調函數會運行多次 —— 每次就緒狀態都會改變 —— 您可以看到每個就緒狀態的警告。這是跟蹤請求所經曆的各個階段的最好方法。 浏覽器的不一致性 在對這個過程有一個基本的了解之後,請試著從幾個不同的浏覽器中訪問您的頁面。您應該會注意到各個浏覽器如何處理這些就緒狀態並不一致。例如,在 Firefox 1.5 中,您會看到以下就緒狀態: ·1 ·2 ·3 ·4 這並不奇怪,因爲每個請求狀態都在這裏表示出來了。然而,如果您使用 Safari 來訪問相同的應用程序,就應該看到 —— 或者看不到 —— 一些有趣的事情。下面是在 Safari 2.0.1 中看到的狀態: ·2 ·3 ·4 Safari 實際上把第一個就緒狀態給丟棄了,也並沒有什麽明顯的原因說明爲什麽要這樣做;不過這就是 Safari 的工作方式。這還說明了一個重要的問題:盡管在使用服務器上的數據之前確保請求的狀態爲 4 是一個好主意,但是依賴于每個過渡期就緒狀態編寫的代碼的確會在不同的浏覽器上得到不同的結果。 例如,在使用 Opera 8.5 時,所顯示的就緒狀態情況就更加糟糕了: ·3 ·4 最後,Internet Explorer 會顯示如下狀態: ·1 ·2 ·3 ·4 如果您碰到請求方面的問題,這就是用來發現問題的 首要之處。最好的方式是在 Internet Explorer 和 Firefox 都進行一下測試 —— 您會看到所有這 4 種狀態,並可以檢查請求的每個狀態所處的情況。 接下來我們再來看一下響應端的情況。 顯微鏡下的響應數據 一旦我們理解在請求過程中發生的各個就緒狀態之後,接下來就可以來看一下 XMLHttpRequest 對象的另外一個方面了 —— responseText 屬性。回想一下在上一篇文章中我們介紹過的內容,就可以知道這個屬性用來從服務器上獲取數據。一旦服務器完成對請求的處理之後,就可以將響應請求數據所需要的任何數據放到請求的 responseText 中了。然後回調函數就可以使用這些數據,如 清單 1 和 清單 4 所示。 清單 4. 使用服務器上返回的響應 function updatePage() { if (request.readyState == 4) { var newTotal = request.responseText; var totalSoldEl = document.getElementById("total-sold"); var netProfitEl = document.getElementById("net-profit"); replaceText(totalSoldEl, newTotal); /* 圖 out the new net profit */ var boardCostEl = document.getElementById("board-cost"); var boardCost = getText(boardCostEl); var manCostEl = document.getElementById("man-cost"); var manCost = getText(manCostEl); var profitPerBoard = boardCost - manCost; var netProfit = profitPerBoard * newTotal; /* Update the net profit on the sales form */ netProfit = Math.round(netProfit * 100) / 100; replaceText(netProfitEl, netProfit); } 清單 1 相當簡單;清單 4 稍微有點複雜,但是它們在開始時都要檢查就緒狀態,並獲取 responseText 屬性的值。 查看請求的響應文本 與就緒狀態類似,responseText 屬性的值在整個請求的生命周期中也會發生變化。要查看這種變化,請使用如 清單 5 所示的代碼來測試請求的響應文本,以及它們的就緒狀態。 清單 5. 測試 responseText 屬性 function updatePage() { // Output the current ready state alert("updatePage() called with ready state of " + request.readyState + " and a response text of '" + request.responseText + "'"); } 現在在浏覽器中打開 Web 應用程序,並激活您的請求。要更好地看到這段代碼的效果,請使用 Firefox 或 Internet Explorer,因爲這兩個浏覽器都可以報告出請求過程中所有可能的就緒狀態。例如在就緒狀態 2 中,就沒有定義 responseText (請參見 圖 3);如果 JavaScript 控制台也已經打開了,您就會看到一個錯誤。 圖 3. 就緒狀態爲 2 的響應文本 不過在就緒狀態 3 中,服務器已經在 responseText 屬性中放上了一個值,至少在這個例子中是這樣(請參見 圖 4)。 圖 4. 就緒狀態爲 3 的響應文本 您會看到就緒狀態爲 3 的響應在每個腳本、每個服務器甚至每個浏覽器上都是不一樣的。不過,這在調試應用程序中依然是非常有用的。 獲取安全數據 所有的文檔和規範都強調,只有在就緒狀態爲 4 時數據才可以安全使用。相信我,當就緒狀態爲 3 時,您很少能找到無法從 responseText 屬性獲取數據的情況。然而,在應用程序中將自己的邏輯依賴于就緒狀態 3 可不是什麽好主意 —— 一旦您編寫了依賴于就緒狀態 3 的完整數據的的代碼,幾乎就要自己來負責當時的數據不完整問題了。 比較好的做法是向用戶提供一些反饋,說明在處于就緒狀態 3 時,很快就會有響應了。盡管使用 alert() 之類的函數顯然不是什麽好主意 —— 使用 Ajax 然後使用一個警告對話框來阻塞用戶顯然是錯誤的 —— 不過您可以在就緒狀態發生變化時更新表單或頁面中的域。例如,對于就緒狀態 1 來說要將進度指示器的寬度設置爲 25%,對于就緒狀態 2 來說要將進度指示器的寬度設置爲 50%,對于就緒狀態 3 來說要將進度指示器的寬度設置爲 75%,當就緒狀態爲 4 時將進度指示器的寬度設置爲 100%(完成)。 當然,正如您已經看到的一樣,這種方法非常聰明,但它是依賴于浏覽器的。在 Opera 上,您永遠都不會看到前兩個就緒狀態,而在 Safari 上則沒有第一個(1)。由于這個原因,我將這段代碼留作練習,而沒有在本文中包括進來。 現在應該來看一下狀態代碼了。 深入了解 HTTP 狀態代碼 有了就緒狀態和您在 Ajax 編程技術中學習到的服務器的響應,您就可以爲 Ajax 應用程序添加另外一級複雜性了 —— 這要使用 HTTP 狀態代碼。這些代碼對于 Ajax 來說並沒有什麽新鮮。從 Web 出現以來,它們就已經存在了。在 Web 浏覽器中您可能已經看到過幾個狀態代碼: ·401:未經授權 ·403:禁止 ·404:沒找到 您可以找到更多的狀態代碼(完整清單請參見 參考資料)。要爲 Ajax 應用程序另外添加一層控制和響應(以及更爲健壯的錯誤處理)機制,您需要適當地查看請求和響應中的狀態代碼。 200:一切正常 在很多 Ajax 應用程序中,您將看到一個回調函數,它負責檢查就緒狀態,然後繼續利用從服務器響應中返回的數據,如 清單 6 所示。 清單 6. 忽略狀態代碼的回調函數 function updatePage() { if (request.readyState == 4) { var response = request.responseText.split("|"); document.getElementById("order").value = response[0]; document.getElementById("address").innerHTML = response[1].replace(/\n/g, "<br />"); } } 這對于 Ajax 編程來說證明是一種短視而錯誤的方法。如果腳本需要認證,而請求卻沒有提供有效的證書,那麽服務器就會返回諸如 403 或 401 之類的錯誤代碼。然而,由于服務器對請求進行了應答,因此就緒狀態就被設置爲 4(即使應答並不是請求所期望的也是如此)。最終,用戶沒有獲得有效數據,當 JavaScript 試圖使用不存在的服務器數據時就可能會出現嚴重的錯誤。 它花費了最小的努力來確保服務器不但完成了一個請求,而且還返回了一個 「一切良好」 的狀態代碼。這個代碼是 "200",它是通過 XMLHttpRequest 對象的 status 屬性來報告的。爲了確保服務器不但完成了一個請求,而且還報告了一個 OK 狀態,請在您的回調函數中添加另外一個檢查功能,如 清單 7 所示。 清單 7. 檢查有效狀態代碼 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, "<br />"); } else alert("status is " + request.status); } } 通過添加這幾行代碼,您就可以確認是否存在問題,用戶會看到一個有用的錯誤消息,而不僅僅是看到一個由斷章取義的數據所構成的頁面,而沒有任何解釋。 重定向和重新路由 在深入介紹有關錯誤的內容之前,我們有必要來討論一下有關一個在使用 Ajax 時 並不需要 關心的問題 —— 重定向。在 HTTP 狀態代碼中,這是 300 系列的狀態代碼,包括: ·301:永久移動 ·302:找到(請求被重新定向到另外一個 URL/URI 上) ·305:使用代理(請求必須使用一個代理來訪問所請求的資源) Ajax 程序員可能並不太關心有關重定向的問題,這是由于兩方面的原因: ·首先,Ajax 應用程序通常都是爲一個特定的服務器端腳本、servlet 或應用程序而編寫的。對于那些您看不到就消失了的組件來說,Ajax 程序員就不太清楚了。因此有時您會知道資源已經移動了(因爲您移動了它,或者通過某種手段移動了它),接下來要修改請求中的 URL,並且不會再碰到這種結果了。 更爲重要的一個原因是:Ajax 應用程序和請求都是封裝在沙盒中的。這就意味著提供生成 Ajax 請求的 Web 頁面的域必須是對這些請求進行響應的域。因此 ebay.com 所提供的 Web 頁面就不能對一個在 amazon.com 上運行的腳本生成一個 Ajax 風格的請求;在 ibm.com 上的 Ajax 應用程序也無法對在 netbeans.org 上運行的 servlets 發出請求。 ·結果是您的請求無法重定向到其他服務器上,而不會産生安全性錯誤。在這些情況中,您根本就不會得到狀態代碼。通常在調試控制台中都會産生一個 JavaScript 錯誤。因此,在對狀態代碼進行充分的考慮之後,您就可以完全忽略重定向代碼的問題了。 結果是您的請求無法重定向到其他服務器上,而不會産生安全性錯誤。在這些情況中,您根本就不會得到狀態代碼。通常在調試控制台中都會産生一個 JavaScript 錯誤。因此,在對狀態代碼進行充分的考慮之後,您就可以完全忽略重定向代碼的問題了。 錯誤 一旦接收到狀態代碼 200 並且意識到可以很大程度上忽略 300 系列的狀態代碼之後,所需要擔心的唯一一組代碼就是 400 系列的代碼了,這說明了不同類型的錯誤。回頭再來看一下 清單 7,並注意在對錯誤進行處理時,只將少數常見的錯誤消息輸出給用戶了。盡管這是朝正確方向前進的一步,但是要告訴從事應用程序開發的用戶和程序員究竟發生了什麽問題,這些消息仍然是沒有太大用處的。 首先,我們要添加對找不到的頁的支持。實際上這在大部分産品系統中都不應該出現,但是在測試腳本位置發生變化或程序員輸入了錯誤的 URL 時,這種情況並不罕見。如果您可以自然地報告 404 錯誤,就可以爲那些困擾不堪的用戶和程序員提供更多幫助。例如,如果服務器上的一個腳本被刪除了,我們就可以使用 清單 7 中的代碼,這樣用戶就會看到一個如 圖 5 所示的非描述性錯誤。 邊界情況和困難情況 看到現在,一些新手程序員就可能會這究竟是要討論什麽內容。有一點事實大家需要知道:只有不到 5% 的 Ajax 請求需要使用諸如 2、3 之類的就緒狀態和諸如 403 之類的狀態代碼(實際上,這個比率可能更接近于 1% 甚至更少)。這些情況非常重要,稱爲 邊界情況(edge case) —— 它們只會在一些非常特殊的情況下發生,其中遇到的都是最奇特的問題。雖然這些情況並不普遍,但是這些邊界情況卻占據了大部分用戶所碰到的問題的 80%! 對于典型的用戶來說,應用程序 100 次都是正常工作的這個事實通常都會被忘記,然而應用程序只要一次出錯就會被他們清楚地記住。如果您可以很好地處理邊界情況(或困難情況),就可以爲再次訪問站點的用戶提供滿意的回報。 圖 5. 常見錯誤處理 用戶無法判斷問題究竟是認證問題、沒找到腳本(此處就是這種情況)、用戶錯誤還是代碼中有些地方産生了問題。添加一些簡單的代碼可以讓這個錯誤更加具體。請參照 清單 8,它負責處理沒找到的腳本或認證發生錯誤的情況,在出現這些錯誤時都會給出具體的消息。 清單 8. 檢查有效狀態代碼 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, "<br />"); } else if (request.status == 404) { alert ("Requested URL is not found."); } else if (request.status == 403) { alert("Access denied."); } else alert("status is " + request.status); } } 雖然這依然相當簡單,但是它的確多提供了一些有用的信息。圖 6 給出了與 圖 5 相同的錯誤,但是這一次錯誤處理代碼向用戶或程序員更好地說明了究竟發生了什麽。 圖 6. 特殊錯誤處理 在我們自己的應用程序中,可以考慮在發生認證失敗的情況時清除用戶名和密碼,並向屏幕上添加一條錯誤消息。我們可以使用類似的方法來更好地處理找不到腳本或其他 400 類型的錯誤(例如 405 表示不允許使用諸如發送 HEAD 請求之類不可接受的請求方法,而 407 則表示需要進行代理認證)。然而不管采用哪種選擇,都需要從對服務器上返回的狀態代碼開始入手進行處理。 其他請求類型 如果您真希望控制 XMLHttpRequest 對象,可以考慮最後實現這種功能 —— 將 HEAD 請求添加到指令中。在前兩篇文章中,我們已經介紹了如何生成 GET 請求;在馬上就要發表的一篇文章中,您會學習有關使用 POST 請求將數據發送到服務器上的知識。不過本著增強錯誤處理和信息搜集的精神,您應該學習如何生成 HEAD 請求。 生成請求 實際上生成 HEAD 請求非常簡單;您可以使用 "HEAD"(而不是 "GET" 或 "POST")作爲第一個參數來調用 open() 方法,如 清單 9 所示。 清單 9. 使用 Ajax 生成一個 HEAD 請求 function getSalesData() { createRequest(); var url = "/boards/servlet/UpdateBoardSales"; request.open("HEAD", url, true); request.onreadystatechange = updatePage; request.send(null); } 當您這樣生成一個 HEAD 請求時,服務器並不會像對 GET 或 POST 請求一樣返回一個真正的響應。相反,服務器只會返回資源的 頭(header),這包括響應中內容最後修改的時間、請求資源是否存在和很多其他有用信息。您可以在服務器處理並返回資源之前使用這些信息來了解有關資源的信息。 對于這種請求您可以做的最簡單的事情就是簡單地輸出所有的響應頭的內容。這可以讓您了解通過 HEAD 請求可以使用什麽。清單 10 提供了一個簡單的回調函數,用來輸出從 HEAD 請求中獲得的響應頭的內容。 清單 10. 輸出從 HEAD 請求中獲得的響應頭的內容 function updatePage() { if (request.readyState == 4) { alert(request.getAllResponseHeaders()); } } 請參見 圖 7,其中顯示了從一個向服務器發出的 HEAD 請求的簡單 Ajax 應用程序返回的響應頭。 您可以單獨使用這些頭(從服務器類型到內容類型)在 Ajax 應用程序中提供其他信息或功能。 檢查 URL 您已經看到了當 URL 不存在時應該如何檢查 404 錯誤。如果這變成一個常見的問題 —— 可能是缺少了一個特定的腳本或 servlet —— 那麽您就可能會希望在生成完整的 GET 或 POST 請求之前來檢查這個 URL。要實現這種功能,生成一個 HEAD 請求,然後在回調函數中檢查 404 錯誤;清單 11 給出了一個簡單的回調函數。 清單 11. 檢查某個 URL 是否存在 function updatePage() { if (request.readyState == 4) { if (request.status == 200) { alert("URL exists"); } else if (request.status == 404) { alert("URL does not exist."); } else { alert("Status is: " + request.status); } } } 誠實地說,這段代碼的價值並不太大。服務器必須對請求進行響應,並構造一個響應來填充內容長度的響應頭,因此並不能節省任何處理時間。另外,這花費的時間與生成請求並使用 HEAD 請求來查看 URL 是否存在所需要的時間一樣多,因爲它要生成使用 GET 或 POST 的請求,而不僅僅是如 清單 7 所示一樣來處理錯誤代碼。不過,有時確切地了解目前什麽可用也是非常有用的;您永遠不會知道何時創造力就會迸發或者何時需要 HEAD 請求! 有用的 HEAD 請求 您會發現 HEAD 請求非常有用的一個領域是用來查看內容的長度或內容的類型。這樣可以確定是否需要發回大量數據來處理請求,和服務器是否試圖返回二進制數據,而不是 HTML、文本或 XML(在 JavaScript 中,這 3 種類型的數據都比二進制數據更容易處理)。 在這些情況中,您只使用了適當的頭名,並將其傳遞給 XMLHttpRequest 對象的 getResponseHeader() 方法。因此要獲取響應的長度,只需要調用 request.getResponseHeader("Content-Length");。要獲取內容類型,請使用 request.getResponseHeader("Content-Type");。 在很多應用程序中,生成 HEAD 請求並沒有增加任何功能,甚至可能會導致請求速度變慢(通過強制生成一個 HEAD 請求來獲取有關響應的數據,然後在使用一個 GET 或 POST 請求來真正獲取響應)。然而,在出現您不確定有關腳本或服務器端組件的情況時,使用 HEAD 請求可以獲取一些基本的數據,而不需要對響應數據真正進行處理,也不需要大量的帶寬來發送響應。 結束語 對于很多 Ajax 和 Web 程序員來說,本文中介紹的內容似乎是太高級了。生成 HEAD 請求的價值是什麽呢?到底在什麽情況下需要在 JavaScript 中顯式地處理重定向狀態代碼呢?這些都是很好的問題;對于簡單的應用程序來說,答案是這些高級技術的價值並不是非常大。 然而,Web 已經不再是只需實現簡單應用程序的地方了;用戶已經變得更加高級,客戶期望能夠獲得更好的穩定性、更高級的錯誤報告,如果應用程序有 1% 的時間停機,那麽經理就可能會因此而被解雇。 因此您的工作就不能僅僅局限于簡單的應用程序了,而是需要更深入理解 XMLHttpRequest。 ·如果您可以考慮各種就緒狀態 —— 並且理解了這些就緒狀態在不同浏覽器之間的區別 —— 就可以快速調試應用程序了。您甚至可以基于就緒狀態而開發一些創造性的功能,並向用戶和客戶回報請求的狀態。 ·如果您要對狀態代碼進行控制,就可以設置應用程序來處理腳本錯誤、非預期的響應以及邊緣情況。結果是應用程序在所有的時間都可以正常工作,而不僅僅是只能一切都正常的情況下才能運行。 ·增加這種生成 HEAD 請求的能力,檢查某個 URL 是否存在,以及確認某個文件是否被修改過,這樣就可以確保用戶可以獲得有效的頁面,用戶所看到的信息都是最新的,(最重要的是)讓他們驚訝這個應用程序是如何健壯和通用。 本文的目的並非是要讓您的應用程序顯得十分華麗,而是幫助您去掉黃色聚光燈後重點昭顯文字的美麗,或者外觀更像桌面一樣。盡管這些都是 Ajax 的功能(在後續幾篇文章中就會介紹),不過它們卻像是蛋糕表面的一層奶油。如果您可以使用 Ajax 來構建一個堅實的基礎,讓應用程序可以很好地處理錯誤和問題,用戶就會返回您的站點和應用程序。在接下來的文章中,我們將添加這種直觀的技巧,這會讓客戶興奮得發抖。(認真地說,您一定不希望錯過下一篇文章!)
󰈣󰈤
 
 
 
>>返回首頁<<
 
 
 
 
 熱帖排行
 
王朝網路微信公眾號
微信掃碼關註本站公眾號 wangchaonetcn
 
  免責聲明:本文僅代表作者個人觀點,與王朝網絡無關。王朝網絡登載此文出於傳遞更多信息之目的,並不意味著贊同其觀點或證實其描述,其原創性以及文中陳述文字和內容未經本站證實,對本文以及其中全部或者部分內容、文字的真實性、完整性、及時性本站不作任何保證或承諾,請讀者僅作參考,並請自行核實相關內容。
 
© 2005- 王朝網路 版權所有