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

 獲取安全數據
所有的文檔和規範都強調,只有在就緒狀態爲 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. 常見錯誤處理

[img]mhtml:file://D:/鳳飛飛/四天學會AJAX/[四天學會ajax] 學習Ajax教程第三天,Ajax 中的高級請求和響應-Ajax基礎教程 by alixixi_com.mht!http://www.blueidea.com/articleimg/2006/04/3395/generic_error.jpg[/img]

用戶無法判斷問題究竟是認證問題、沒找到腳本(此處就是這種情況)、用戶錯誤還是代碼中有些地方產生了問題。添加一些簡單的代碼可以讓這個錯誤更加具體。請參照 清單 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. 特殊錯誤處理

[img]mhtml:file://D:/鳳飛飛/四天學會AJAX/[四天學會ajax] 學習Ajax教程第三天,Ajax 中的高級請求和響應-Ajax基礎教程 by alixixi_com.mht!http://www.blueidea.com/articleimg/2006/04/3395/specific_error.jpg[/img]

在我們自己的應用程序中,可以考慮在發生認證失敗的情況時清除用戶名和密碼,並向屏幕上添加一條錯誤消息。我們可以使用類似的方法來更好地處理找不到腳本或其他 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 應用程序返回的響應頭。

[img]mhtml:file://D:/鳳飛飛/四天學會AJAX/[四天學會ajax] 學習Ajax教程第三天,Ajax 中的高級請求和響應-Ajax基礎教程 by alixixi_com.mht!http://www.blueidea.com/articleimg/2006/04/3395/response_headers.jpg[/img]

您可以單獨使用這些頭(從服務器類型到內容類型)在 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 來構建一個堅實的基礎,讓應用程序可以很好地處理錯誤和問題,用戶就會返回您的站點和應用程序。在接下來的文章中,我們將添加這種直觀的技巧,這會讓客戶興奮得發抖。

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