<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    gembin

    OSGi, Eclipse Equinox, ECF, Virgo, Gemini, Apache Felix, Karaf, Aires, Camel, Eclipse RCP

    HBase, Hadoop, ZooKeeper, Cassandra

    Flex4, AS3, Swiz framework, GraniteDS, BlazeDS etc.

    There is nothing that software can't fix. Unfortunately, there is also nothing that software can't completely fuck up. That gap is called talent.

    About Me

     

    Ajax 中的高級(jí)請(qǐng)求和響應(yīng)[zhuan]

    對(duì)于很多 Web 開(kāi)發(fā)人員來(lái)說(shuō),只需要生成簡(jiǎn)單的請(qǐng)求并接收簡(jiǎn)單的響應(yīng)即可;但是對(duì)于希望掌握 Ajax 的開(kāi)發(fā)人員來(lái)說(shuō),必須要全面理解 HTTP 狀態(tài)代碼、就緒狀態(tài)和 XMLHttpRequest 對(duì)象。在本文中,Brett McLaughlin 將向您介紹各種狀態(tài)代碼,并展示瀏覽器如何對(duì)其進(jìn)行處理,本文還給出了在 Ajax 中使用的比較少見(jiàn)的 HTTP 請(qǐng)求。

    在本系列的 上篇文章 中,我們將詳細(xì)介紹 XMLHttpRequest 對(duì)象,它是 Ajax 應(yīng)用程序的中心,負(fù)責(zé)處理服務(wù)器端應(yīng)用程序和腳本的請(qǐng)求,并處理從服務(wù)器端組件返回的數(shù)據(jù)。由于所有的 Ajax 應(yīng)用程序都要使用 XMLHttpRequest 對(duì)象,因此您可能會(huì)希望熟悉這個(gè)對(duì)象,從而能夠讓 Ajax 執(zhí)行得更好。

    在本文中,我將在上一篇文章的基礎(chǔ)上重點(diǎn)介紹這個(gè)請(qǐng)求對(duì)象的 3 個(gè)關(guān)鍵部分的內(nèi)容:

    • HTTP 就緒狀態(tài)
    • HTTP 狀態(tài)代碼
    • 可以生成的請(qǐng)求類(lèi)型

    這三部分內(nèi)容都是在構(gòu)造一個(gè)請(qǐng)求時(shí)所要考慮的因素;但是介紹這些主題的內(nèi)容太少了。然而,如果您不僅僅是想了解 Ajax 編程的常識(shí),而是希望了解更多內(nèi)容,就需要熟悉就緒狀態(tài)、狀態(tài)代碼和請(qǐng)求本身的內(nèi)容。當(dāng)應(yīng)用程序出現(xiàn)問(wèn)題時(shí) —— 這種問(wèn)題總是存在 —— 那么如果能夠正確理解就緒狀態(tài)、如何生成一個(gè) HEAD 請(qǐng)求或者 400 的狀態(tài)代碼的確切含義,就可以在 5 分鐘內(nèi)調(diào)試出問(wèn)題,而不是在各種挫折和困惑中度過(guò) 5 個(gè)小時(shí)。

    XMLHttpRequest 或 XMLHttp:換名玫瑰

    Microsoft™ 和 Internet Explorer 使用了一個(gè)名為 XMLHttp 的對(duì)象,而不是 XMLHttpRequest 對(duì)象,而 Mozilla、Opera、Safari 和 大部分非 Microsoft 瀏覽器都使用的是后者。為了簡(jiǎn)單性起見(jiàn),我將這兩個(gè)對(duì)象都簡(jiǎn)單地稱為 XMLHttpRequest。這既符合我們?cè)?Web 上看到的情況,又符合 Microsoft 在 Internet Explorer 7.0 中使用 XMLHttpRequest 作為請(qǐng)求對(duì)象的意圖。(有關(guān)這個(gè)問(wèn)題的更多內(nèi)容,請(qǐng)參見(jiàn) 第 2 部分。)

    下面讓我們首先來(lái)看一下 HTTP 就緒狀態(tài)。

    深入了解 HTTP 就緒狀態(tài)

    您應(yīng)該還記得在上一篇文章中 XMLHttpRequest 對(duì)象有一個(gè)名為 readyState 的屬性。這個(gè)屬性確保服務(wù)器已經(jīng)完成了一個(gè)請(qǐng)求,通常會(huì)使用一個(gè)回調(diào)函數(shù)從服務(wù)器中讀出數(shù)據(jù)來(lái)更新 Web 表單或頁(yè)面的內(nèi)容。清單 1 給出了一個(gè)簡(jiǎn)單的例子(這也是本系列的上一篇文章中的一個(gè)例子 —— 請(qǐng)參見(jiàn) 參考資料)。


    清單 1. 在回調(diào)函數(shù)中處理服務(wù)器的響應(yīng)
     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);
    }
    }

    這顯然是就緒狀態(tài)最常見(jiàn)(也是最簡(jiǎn)單)的用法。正如您從數(shù)字 "4" 中可以看出的一樣,還有其他幾個(gè)就緒狀態(tài)(您在上一篇文章中也看到過(guò)這個(gè)清單 —— 請(qǐng)參見(jiàn) 參考資料):

    • 0:請(qǐng)求未初始化(還沒(méi)有調(diào)用 open())。
    • 1:請(qǐng)求已經(jīng)建立,但是還沒(méi)有發(fā)送(還沒(méi)有調(diào)用 send())。
    • 2:請(qǐng)求已發(fā)送,正在處理中(通常現(xiàn)在可以從響應(yīng)中獲取內(nèi)容頭)。
    • 3:請(qǐng)求在處理中;通常響應(yīng)中已有部分?jǐn)?shù)據(jù)可用了,但是服務(wù)器還沒(méi)有完成響應(yīng)的生成。
    • 4:響應(yīng)已完成;您可以獲取并使用服務(wù)器的響應(yīng)了。

    如果您希望不僅僅是了解 Ajax 編程的基本知識(shí),那么就不但需要知道這些狀態(tài),了解這些狀態(tài)是何時(shí)出現(xiàn)的,以及如何來(lái)使用這些狀態(tài)。首先,您需要學(xué)習(xí)在每種就緒狀態(tài)下可能碰到的是哪種請(qǐng)求狀態(tài)。不幸的是,這一點(diǎn)并不直觀,而且會(huì)涉及幾種特殊的情況。

    隱秘就緒狀態(tài)

    第一種就緒狀態(tài)的特點(diǎn)是 readyState 屬性為 0(readyState == 0),表示未初始化狀態(tài)。一旦對(duì)請(qǐng)求對(duì)象調(diào)用 open() 之后,這個(gè)屬性就被設(shè)置為 1。由于您通常都是在一對(duì)請(qǐng)求進(jìn)行初始化之后就立即調(diào)用 open(),因此很少會(huì)看到 readyState == 0 的狀態(tài)。另外,未初始化的就緒狀態(tài)在實(shí)際的應(yīng)用程序中是沒(méi)有真正的用處的。

    不過(guò)為了滿足我們的興趣,請(qǐng)參見(jiàn) 清單 2 的內(nèi)容,其中顯示了如何在 readyState 被設(shè)置為 0 時(shí)來(lái)獲取這種就緒狀態(tài)。


    清單 2. 獲取 0 就緒狀態(tài)
       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);
    }

    在這個(gè)簡(jiǎn)單的例子中,getSalesData() 是 Web 頁(yè)面調(diào)用來(lái)啟動(dòng)請(qǐng)求(例如點(diǎn)擊一個(gè)按鈕時(shí))所使用的函數(shù)。注意您必須在調(diào)用 open() 之前 來(lái)查看就緒狀態(tài)。圖 1 給出了運(yùn)行這個(gè)應(yīng)用程序的結(jié)果。


    圖 1. 就緒狀態(tài) 0
    就緒狀態(tài) 0
    當(dāng) 0 等于 4 時(shí)

    在多個(gè) JavaScript 函數(shù)都使用相同的請(qǐng)求對(duì)象時(shí),您需要檢查就緒狀態(tài) 0 來(lái)確保這個(gè)請(qǐng)求對(duì)象沒(méi)有正在使用,這種機(jī)制會(huì)產(chǎn)生問(wèn)題。由于 readyState == 4 表示一個(gè)已完成的請(qǐng)求,因此您經(jīng)常會(huì)發(fā)現(xiàn)那些目前沒(méi)在使用的處于就緒狀態(tài)的請(qǐng)求對(duì)象仍然被設(shè)置成了 4 —— 這是因?yàn)閺姆?wù)器返回來(lái)的數(shù)據(jù)已經(jīng)使用過(guò)了,但是從它們被設(shè)置為就緒狀態(tài)之后就沒(méi)有進(jìn)行任何變化。有一個(gè)函數(shù) abort() 會(huì)重新設(shè)置請(qǐng)求對(duì)象,但是這個(gè)函數(shù)卻不是真正為了這個(gè)目的而使用的。如果您 必須 使用多個(gè)函數(shù),最好是為每個(gè)函數(shù)都創(chuàng)建并使用一個(gè)函數(shù),而不是在多個(gè)函數(shù)之間共享相同的對(duì)象。

    顯然,這并不能為您帶來(lái)多少好處;需要確保 尚未 調(diào)用 open() 函數(shù)的情況很少。在大部分 Ajax 編程的真實(shí)情況中,這種就緒狀態(tài)的唯一用法就是使用相同的 XMLHttpRequest 對(duì)象在多個(gè)函數(shù)之間生成多個(gè)請(qǐng)求。在這種(不常見(jiàn)的)情況中,您可能會(huì)在生成新請(qǐng)求之前希望確保請(qǐng)求對(duì)象是處于未初始化狀態(tài)(readyState == 0)。這實(shí)際上是要確保另外一個(gè)函數(shù)沒(méi)有同時(shí)使用這個(gè)對(duì)象。

    查看正在處理的請(qǐng)求的就緒狀態(tài)

    除了 0 就緒狀態(tài)之外,請(qǐng)求對(duì)象還需要依次經(jīng)歷典型的請(qǐng)求和響應(yīng)的其他幾種就緒狀態(tài),最后才以就緒狀態(tài) 4 的形式結(jié)束。這就是為什么您在大部分回調(diào)函數(shù)中都可以看到 if (request.readyState == 4) 這行代碼;它確保服務(wù)器已經(jīng)完成對(duì)請(qǐng)求的處理,現(xiàn)在可以安全地更新 Web 頁(yè)面或根據(jù)從服務(wù)器返回來(lái)的數(shù)據(jù)來(lái)進(jìn)行操作了。

    要查看這種狀態(tài)發(fā)生的過(guò)程非常簡(jiǎn)單。如果就緒狀態(tài)為 4,我們不僅要運(yùn)行回調(diào)函數(shù)中的代碼,而且還要在每次調(diào)用回調(diào)函數(shù)時(shí)都輸出就緒狀態(tài)。 清單 3 給出了一個(gè)實(shí)現(xiàn)這種功能的例子。


    清單 3. 查看就緒狀態(tài)
       function updatePage() {
    // Output the current ready state
    alert("updatePage() called with ready state of " + request.readyState);
    }

    如果您不確定如何運(yùn)行這個(gè)函數(shù),就需要?jiǎng)?chuàng)建一個(gè)函數(shù),然后在 Web 頁(yè)面中調(diào)用這個(gè)函數(shù),并讓它向服務(wù)器端的組件發(fā)送一個(gè)請(qǐng)求(例如 清單 2 給出的函數(shù),或本系列文章的第 1 部分和第 2 部分中給出的例子)。確保在建立請(qǐng)求時(shí),將回調(diào)函數(shù)設(shè)置為 updatePage();要實(shí)現(xiàn)這種設(shè)置,可以將請(qǐng)求對(duì)象的 onreadystatechange 屬性設(shè)置為 updatePage()

    這段代碼就是 onreadystatechange 意義的一個(gè)確切展示 —— 每次請(qǐng)求的就緒狀態(tài)發(fā)生變化時(shí),就調(diào)用 updatePage(),然后我們就可以看到一個(gè)警告了。圖 2 給出了一個(gè)調(diào)用這個(gè)函數(shù)的例子,其中就緒狀態(tài)為 1。


    圖 2. 就緒狀態(tài) 1
    就緒狀態(tài) 1

    您可以自己嘗試運(yùn)行這段代碼。將其放入 Web 頁(yè)面中,然后激活事件處理程序(單擊按鈕,在域之間按 tab 鍵切換焦點(diǎn),或者使用設(shè)置的任何方法來(lái)觸發(fā)請(qǐng)求)。這個(gè)回調(diào)函數(shù)會(huì)運(yùn)行多次 —— 每次就緒狀態(tài)都會(huì)改變 —— 您可以看到每個(gè)就緒狀態(tài)的警告。這是跟蹤請(qǐng)求所經(jīng)歷的各個(gè)階段的最好方法。

    瀏覽器的不一致性

    在對(duì)這個(gè)過(guò)程有一個(gè)基本的了解之后,請(qǐng)?jiān)囍鴱膸讉€(gè)不同的瀏覽器中訪問(wèn)您的頁(yè)面。您應(yīng)該會(huì)注意到各個(gè)瀏覽器如何處理這些就緒狀態(tài)并不一致。例如,在 Firefox 1.5 中,您會(huì)看到以下就緒狀態(tài):

    • 1
    • 2
    • 3
    • 4

    這并不奇怪,因?yàn)槊總€(gè)請(qǐng)求狀態(tài)都在這里表示出來(lái)了。然而,如果您使用 Safari 來(lái)訪問(wèn)相同的應(yīng)用程序,就應(yīng)該看到 —— 或者看不到 —— 一些有趣的事情。下面是在 Safari 2.0.1 中看到的狀態(tài):

    • 2
    • 3
    • 4

    Safari 實(shí)際上把第一個(gè)就緒狀態(tài)給丟棄了,也并沒(méi)有什么明顯的原因說(shuō)明為什么要這樣做;不過(guò)這就是 Safari 的工作方式。這還說(shuō)明了一個(gè)重要的問(wèn)題:盡管在使用服務(wù)器上的數(shù)據(jù)之前確保請(qǐng)求的狀態(tài)為 4 是一個(gè)好主意,但是依賴于每個(gè)過(guò)渡期就緒狀態(tài)編寫(xiě)的代碼的確會(huì)在不同的瀏覽器上得到不同的結(jié)果

    例如,在使用 Opera 8.5 時(shí),所顯示的就緒狀態(tài)情況就更加糟糕了:

    • 3
    • 4

    最后,Internet Explorer 會(huì)顯示如下?tīng)顟B(tài):

    • 1
    • 2
    • 3
    • 4

    如果您碰到請(qǐng)求方面的問(wèn)題,這就是用來(lái)發(fā)現(xiàn)問(wèn)題的 首要之處。最好的方式是在 Internet Explorer 和 Firefox 都進(jìn)行一下測(cè)試 —— 您會(huì)看到所有這 4 種狀態(tài),并可以檢查請(qǐng)求的每個(gè)狀態(tài)所處的情況。

    接下來(lái)我們?cè)賮?lái)看一下響應(yīng)端的情況。

    顯微鏡下的響應(yīng)數(shù)據(jù)

    一旦我們理解在請(qǐng)求過(guò)程中發(fā)生的各個(gè)就緒狀態(tài)之后,接下來(lái)就可以來(lái)看一下 XMLHttpRequest 對(duì)象的另外一個(gè)方面了 —— responseText 屬性。回想一下在上一篇文章中我們介紹過(guò)的內(nèi)容,就可以知道這個(gè)屬性用來(lái)從服務(wù)器上獲取數(shù)據(jù)。一旦服務(wù)器完成對(duì)請(qǐng)求的處理之后,就可以將響應(yīng)請(qǐng)求數(shù)據(jù)所需要的任何數(shù)據(jù)放到請(qǐng)求的 responseText 中了。然后回調(diào)函數(shù)就可以使用這些數(shù)據(jù),如 清單 1清單 4 所示。


    清單 4. 使用服務(wù)器上返回的響應(yīng)
       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 相當(dāng)簡(jiǎn)單;清單 4 稍微有點(diǎn)復(fù)雜,但是它們?cè)陂_(kāi)始時(shí)都要檢查就緒狀態(tài),并獲取 responseText 屬性的值。

    查看請(qǐng)求的響應(yīng)文本

    與就緒狀態(tài)類(lèi)似,responseText 屬性的值在整個(gè)請(qǐng)求的生命周期中也會(huì)發(fā)生變化。要查看這種變化,請(qǐng)使用如 清單 5 所示的代碼來(lái)測(cè)試請(qǐng)求的響應(yīng)文本,以及它們的就緒狀態(tài)。


    清單 5. 測(cè)試 responseText 屬性
       function updatePage() {
    // Output the current ready state
    alert("updatePage() called with ready state of " + request.readyState +
    " and a response text of '" + request.responseText + "'");
    }

    現(xiàn)在在瀏覽器中打開(kāi) Web 應(yīng)用程序,并激活您的請(qǐng)求。要更好地看到這段代碼的效果,請(qǐng)使用 Firefox 或 Internet Explorer,因?yàn)檫@兩個(gè)瀏覽器都可以報(bào)告出請(qǐng)求過(guò)程中所有可能的就緒狀態(tài)。例如在就緒狀態(tài) 2 中,就沒(méi)有定義 responseText (請(qǐng)參見(jiàn) 圖 3);如果 JavaScript 控制臺(tái)也已經(jīng)打開(kāi)了,您就會(huì)看到一個(gè)錯(cuò)誤。


    圖 3. 就緒狀態(tài)為 2 的響應(yīng)文本
    就緒狀態(tài)為 2 的響應(yīng)文本

    不過(guò)在就緒狀態(tài) 3 中,服務(wù)器已經(jīng)在 responseText 屬性中放上了一個(gè)值,至少在這個(gè)例子中是這樣(請(qǐng)參見(jiàn) 圖 4)。


    圖 4. 就緒狀態(tài)為 3 的響應(yīng)文本
    就緒狀態(tài)為 3 的響應(yīng)文本

    您會(huì)看到就緒狀態(tài)為 3 的響應(yīng)在每個(gè)腳本、每個(gè)服務(wù)器甚至每個(gè)瀏覽器上都是不一樣的。不過(guò),這在調(diào)試應(yīng)用程序中依然是非常有用的。

    獲取安全數(shù)據(jù)

    所有的文檔和規(guī)范都強(qiáng)調(diào),只有在就緒狀態(tài)為 4 時(shí)數(shù)據(jù)才可以安全使用。相信我,當(dāng)就緒狀態(tài)為 3 時(shí),您很少能找到無(wú)法從 responseText 屬性獲取數(shù)據(jù)的情況。然而,在應(yīng)用程序中將自己的邏輯依賴于就緒狀態(tài) 3 可不是什么好主意 —— 一旦您編寫(xiě)了依賴于就緒狀態(tài) 3 的完整數(shù)據(jù)的的代碼,幾乎就要自己來(lái)負(fù)責(zé)當(dāng)時(shí)的數(shù)據(jù)不完整問(wèn)題了。

    比較好的做法是向用戶提供一些反饋,說(shuō)明在處于就緒狀態(tài) 3 時(shí),很快就會(huì)有響應(yīng)了。盡管使用 alert() 之類(lèi)的函數(shù)顯然不是什么好主意 —— 使用 Ajax 然后使用一個(gè)警告對(duì)話框來(lái)阻塞用戶顯然是錯(cuò)誤的 —— 不過(guò)您可以在就緒狀態(tài)發(fā)生變化時(shí)更新表單或頁(yè)面中的域。例如,對(duì)于就緒狀態(tài) 1 來(lái)說(shuō)要將進(jìn)度指示器的寬度設(shè)置為 25%,對(duì)于就緒狀態(tài) 2 來(lái)說(shuō)要將進(jìn)度指示器的寬度設(shè)置為 50%,對(duì)于就緒狀態(tài) 3 來(lái)說(shuō)要將進(jìn)度指示器的寬度設(shè)置為 75%,當(dāng)就緒狀態(tài)為 4 時(shí)將進(jìn)度指示器的寬度設(shè)置為 100%(完成)。

    當(dāng)然,正如您已經(jīng)看到的一樣,這種方法非常聰明,但它是依賴于瀏覽器的。在 Opera 上,您永遠(yuǎn)都不會(huì)看到前兩個(gè)就緒狀態(tài),而在 Safari 上則沒(méi)有第一個(gè)(1)。由于這個(gè)原因,我將這段代碼留作練習(xí),而沒(méi)有在本文中包括進(jìn)來(lái)。

    現(xiàn)在應(yīng)該來(lái)看一下?tīng)顟B(tài)代碼了。

    深入了解 HTTP 狀態(tài)代碼

    有了就緒狀態(tài)和您在 Ajax 編程技術(shù)中學(xué)習(xí)到的服務(wù)器的響應(yīng),您就可以為 Ajax 應(yīng)用程序添加另外一級(jí)復(fù)雜性了 —— 這要使用 HTTP 狀態(tài)代碼。這些代碼對(duì)于 Ajax 來(lái)說(shuō)并沒(méi)有什么新鮮。從 Web 出現(xiàn)以來(lái),它們就已經(jīng)存在了。在 Web 瀏覽器中您可能已經(jīng)看到過(guò)幾個(gè)狀態(tài)代碼:

    • 401:未經(jīng)授權(quán)
    • 403:禁止
    • 404:沒(méi)找到

    您可以找到更多的狀態(tài)代碼(完整清單請(qǐng)參見(jiàn) 參考資料)。要為 Ajax 應(yīng)用程序另外添加一層控制和響應(yīng)(以及更為健壯的錯(cuò)誤處理)機(jī)制,您需要適當(dāng)?shù)夭榭凑?qǐng)求和響應(yīng)中的狀態(tài)代碼。

    200:一切正常

    在很多 Ajax 應(yīng)用程序中,您將看到一個(gè)回調(diào)函數(shù),它負(fù)責(zé)檢查就緒狀態(tài),然后繼續(xù)利用從服務(wù)器響應(yīng)中返回的數(shù)據(jù),如 清單 6 所示。


    清單 6. 忽略狀態(tài)代碼的回調(diào)函數(shù)
       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 />");
    }
    }

    這對(duì)于 Ajax 編程來(lái)說(shuō)證明是一種短視而錯(cuò)誤的方法。如果腳本需要認(rèn)證,而請(qǐng)求卻沒(méi)有提供有效的證書(shū),那么服務(wù)器就會(huì)返回諸如 403 或 401 之類(lèi)的錯(cuò)誤代碼。然而,由于服務(wù)器對(duì)請(qǐng)求進(jìn)行了應(yīng)答,因此就緒狀態(tài)就被設(shè)置為 4(即使應(yīng)答并不是請(qǐng)求所期望的也是如此)。最終,用戶沒(méi)有獲得有效數(shù)據(jù),當(dāng) JavaScript 試圖使用不存在的服務(wù)器數(shù)據(jù)時(shí)就可能會(huì)出現(xiàn)嚴(yán)重的錯(cuò)誤。

    它花費(fèi)了最小的努力來(lái)確保服務(wù)器不但完成了一個(gè)請(qǐng)求,而且還返回了一個(gè) “一切良好” 的狀態(tài)代碼。這個(gè)代碼是 "200",它是通過(guò) XMLHttpRequest 對(duì)象的 status 屬性來(lái)報(bào)告的。為了確保服務(wù)器不但完成了一個(gè)請(qǐng)求,而且還報(bào)告了一個(gè) OK 狀態(tài),請(qǐng)?jiān)谀幕卣{(diào)函數(shù)中添加另外一個(gè)檢查功能,如 清單 7 所示。


    清單 7. 檢查有效狀態(tài)代碼
       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);

    }
    }

    通過(guò)添加這幾行代碼,您就可以確認(rèn)是否存在問(wèn)題,用戶會(huì)看到一個(gè)有用的錯(cuò)誤消息,而不僅僅是看到一個(gè)由斷章取義的數(shù)據(jù)所構(gòu)成的頁(yè)面,而沒(méi)有任何解釋。

    重定向和重新路由

    在深入介紹有關(guān)錯(cuò)誤的內(nèi)容之前,我們有必要來(lái)討論一下有關(guān)一個(gè)在使用 Ajax 時(shí) 并不需要 關(guān)心的問(wèn)題 —— 重定向。在 HTTP 狀態(tài)代碼中,這是 300 系列的狀態(tài)代碼,包括:

    • 301:永久移動(dòng)
    • 302:找到(請(qǐng)求被重新定向到另外一個(gè) URL/URI 上)
    • 305:使用代理(請(qǐng)求必須使用一個(gè)代理來(lái)訪問(wèn)所請(qǐng)求的資源)

    Ajax 程序員可能并不太關(guān)心有關(guān)重定向的問(wèn)題,這是由于兩方面的原因:

    • 首先,Ajax 應(yīng)用程序通常都是為一個(gè)特定的服務(wù)器端腳本、servlet 或應(yīng)用程序而編寫(xiě)的。對(duì)于那些您看不到就消失了的組件來(lái)說(shuō),Ajax 程序員就不太清楚了。因此有時(shí)您會(huì)知道資源已經(jīng)移動(dòng)了(因?yàn)槟苿?dòng)了它,或者通過(guò)某種手段移動(dòng)了它),接下來(lái)要修改請(qǐng)求中的 URL,并且不會(huì)再碰到這種結(jié)果了。
    • 更為重要的一個(gè)原因是:Ajax 應(yīng)用程序和請(qǐng)求都是封裝在沙盒中的。這就意味著提供生成 Ajax 請(qǐng)求的 Web 頁(yè)面的域必須是對(duì)這些請(qǐng)求進(jìn)行響應(yīng)的域。因此 ebay.com 所提供的 Web 頁(yè)面就不能對(duì)一個(gè)在 amazon.com 上運(yùn)行的腳本生成一個(gè) Ajax 風(fēng)格的請(qǐng)求;在 ibm.com 上的 Ajax 應(yīng)用程序也無(wú)法對(duì)在 netbeans.org 上運(yùn)行的 servlets 發(fā)出請(qǐng)求。

    結(jié)果是您的請(qǐng)求無(wú)法重定向到其他服務(wù)器上,而不會(huì)產(chǎn)生安全性錯(cuò)誤。在這些情況中,您根本就不會(huì)得到狀態(tài)代碼。通常在調(diào)試控制臺(tái)中都會(huì)產(chǎn)生一個(gè) JavaScript 錯(cuò)誤。因此,在對(duì)狀態(tài)代碼進(jìn)行充分的考慮之后,您就可以完全忽略重定向代碼的問(wèn)題了。

    邊界情況和困難情況

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

    對(duì)于典型的用戶來(lái)說(shuō),應(yīng)用程序 100 次都是正常工作的這個(gè)事實(shí)通常都會(huì)被忘記,然而應(yīng)用程序只要一次出錯(cuò)就會(huì)被他們清楚地記住。如果您可以很好地處理邊界情況(或困難情況),就可以為再次訪問(wèn)站點(diǎn)的用戶提供滿意的回報(bào)。

    錯(cuò)誤

    一旦接收到狀態(tài)代碼 200 并且意識(shí)到可以很大程度上忽略 300 系列的狀態(tài)代碼之后,所需要擔(dān)心的唯一一組代碼就是 400 系列的代碼了,這說(shuō)明了不同類(lèi)型的錯(cuò)誤。回頭再來(lái)看一下 清單 7,并注意在對(duì)錯(cuò)誤進(jìn)行處理時(shí),只將少數(shù)常見(jiàn)的錯(cuò)誤消息輸出給用戶了。盡管這是朝正確方向前進(jìn)的一步,但是要告訴從事應(yīng)用程序開(kāi)發(fā)的用戶和程序員究竟發(fā)生了什么問(wèn)題,這些消息仍然是沒(méi)有太大用處的。

    首先,我們要添加對(duì)找不到的頁(yè)的支持。實(shí)際上這在大部分產(chǎn)品系統(tǒng)中都不應(yīng)該出現(xiàn),但是在測(cè)試腳本位置發(fā)生變化或程序員輸入了錯(cuò)誤的 URL 時(shí),這種情況并不罕見(jiàn)。如果您可以自然地報(bào)告 404 錯(cuò)誤,就可以為那些困擾不堪的用戶和程序員提供更多幫助。例如,如果服務(wù)器上的一個(gè)腳本被刪除了,我們就可以使用 清單 7 中的代碼,這樣用戶就會(huì)看到一個(gè)如 圖 5 所示的非描述性錯(cuò)誤。


    圖 5. 常見(jiàn)錯(cuò)誤處理
    常見(jiàn)錯(cuò)誤處理

    用戶無(wú)法判斷問(wèn)題究竟是認(rèn)證問(wèn)題、沒(méi)找到腳本(此處就是這種情況)、用戶錯(cuò)誤還是代碼中有些地方產(chǎn)生了問(wèn)題。添加一些簡(jiǎn)單的代碼可以讓這個(gè)錯(cuò)誤更加具體。請(qǐng)參照 清單 8,它負(fù)責(zé)處理沒(méi)找到的腳本或認(rèn)證發(fā)生錯(cuò)誤的情況,在出現(xiàn)這些錯(cuò)誤時(shí)都會(huì)給出具體的消息。


    清單 8. 檢查有效狀態(tài)代碼
       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);
    }
    }

    雖然這依然相當(dāng)簡(jiǎn)單,但是它的確多提供了一些有用的信息。圖 6 給出了與 圖 5 相同的錯(cuò)誤,但是這一次錯(cuò)誤處理代碼向用戶或程序員更好地說(shuō)明了究竟發(fā)生了什么。


    圖 6. 特殊錯(cuò)誤處理
    特殊錯(cuò)誤處理

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

    其他請(qǐng)求類(lèi)型

    如果您真希望控制 XMLHttpRequest 對(duì)象,可以考慮最后實(shí)現(xiàn)這種功能 —— 將 HEAD 請(qǐng)求添加到指令中。在前兩篇文章中,我們已經(jīng)介紹了如何生成 GET 請(qǐng)求;在馬上就要發(fā)表的一篇文章中,您會(huì)學(xué)習(xí)有關(guān)使用 POST 請(qǐng)求將數(shù)據(jù)發(fā)送到服務(wù)器上的知識(shí)。不過(guò)本著增強(qiáng)錯(cuò)誤處理和信息搜集的精神,您應(yīng)該學(xué)習(xí)如何生成 HEAD 請(qǐng)求。

    生成請(qǐng)求

    實(shí)際上生成 HEAD 請(qǐng)求非常簡(jiǎn)單;您可以使用 "HEAD"(而不是 "GET" 或 "POST")作為第一個(gè)參數(shù)來(lái)調(diào)用 open() 方法,如 清單 9 所示。


    清單 9. 使用 Ajax 生成一個(gè) HEAD 請(qǐng)求
       function getSalesData() {
    createRequest();
    var url = "/boards/servlet/UpdateBoardSales";
    request.open("HEAD", url, true);
    request.onreadystatechange = updatePage;
    request.send(null);
    }

    當(dāng)您這樣生成一個(gè) HEAD 請(qǐng)求時(shí),服務(wù)器并不會(huì)像對(duì) GET 或 POST 請(qǐng)求一樣返回一個(gè)真正的響應(yīng)。相反,服務(wù)器只會(huì)返回資源的 頭(header),這包括響應(yīng)中內(nèi)容最后修改的時(shí)間、請(qǐng)求資源是否存在和很多其他有用信息。您可以在服務(wù)器處理并返回資源之前使用這些信息來(lái)了解有關(guān)資源的信息。

    對(duì)于這種請(qǐng)求您可以做的最簡(jiǎn)單的事情就是簡(jiǎn)單地輸出所有的響應(yīng)頭的內(nèi)容。這可以讓您了解通過(guò) HEAD 請(qǐng)求可以使用什么。清單 10 提供了一個(gè)簡(jiǎn)單的回調(diào)函數(shù),用來(lái)輸出從 HEAD 請(qǐng)求中獲得的響應(yīng)頭的內(nèi)容。


    清單 10. 輸出從 HEAD 請(qǐng)求中獲得的響應(yīng)頭的內(nèi)容
       function updatePage() {
    if (request.readyState == 4) {
    alert(request.getAllResponseHeaders());
    }
    }

    請(qǐng)參見(jiàn) 圖 7,其中顯示了從一個(gè)向服務(wù)器發(fā)出的 HEAD 請(qǐng)求的簡(jiǎn)單 Ajax 應(yīng)用程序返回的響應(yīng)頭。


    圖 7. HEAD 請(qǐng)求的響應(yīng)頭
    HEAD 請(qǐng)求的響應(yīng)頭

    您可以單獨(dú)使用這些頭(從服務(wù)器類(lèi)型到內(nèi)容類(lèi)型)在 Ajax 應(yīng)用程序中提供其他信息或功能。

    檢查 URL

    您已經(jīng)看到了當(dāng) URL 不存在時(shí)應(yīng)該如何檢查 404 錯(cuò)誤。如果這變成一個(gè)常見(jiàn)的問(wèn)題 —— 可能是缺少了一個(gè)特定的腳本或 servlet —— 那么您就可能會(huì)希望在生成完整的 GET 或 POST 請(qǐng)求之前來(lái)檢查這個(gè) URL。要實(shí)現(xiàn)這種功能,生成一個(gè) HEAD 請(qǐng)求,然后在回調(diào)函數(shù)中檢查 404 錯(cuò)誤;清單 11 給出了一個(gè)簡(jiǎn)單的回調(diào)函數(shù)。


    清單 11. 檢查某個(gè) 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);
    }
    }
    }

    誠(chéng)實(shí)地說(shuō),這段代碼的價(jià)值并不太大。服務(wù)器必須對(duì)請(qǐng)求進(jìn)行響應(yīng),并構(gòu)造一個(gè)響應(yīng)來(lái)填充內(nèi)容長(zhǎng)度的響應(yīng)頭,因此并不能節(jié)省任何處理時(shí)間。另外,這花費(fèi) 的時(shí)間與生成請(qǐng)求并使用 HEAD 請(qǐng)求來(lái)查看 URL 是否存在所需要的時(shí)間一樣多,因?yàn)樗墒褂?GET 或 POST 的請(qǐng)求,而不僅僅是如 清單 7 所示一樣來(lái)處理錯(cuò)誤代碼。不過(guò),有時(shí)確切地了解目前什么可用也是非常有用的;您永遠(yuǎn)不會(huì)知道何時(shí)創(chuàng)造力就會(huì)迸發(fā)或者何時(shí)需要 HEAD 請(qǐng)求!

    有用的 HEAD 請(qǐng)求

    您會(huì)發(fā)現(xiàn) HEAD 請(qǐng)求非常有用的一個(gè)領(lǐng)域是用來(lái)查看內(nèi)容的長(zhǎng)度或內(nèi)容的類(lèi)型。這樣可以確定是否需要發(fā)回大量數(shù)據(jù)來(lái)處理請(qǐng)求,和服務(wù)器是否試圖返回二進(jìn)制數(shù)據(jù),而不是 HTML、文本或 XML(在 JavaScript 中,這 3 種類(lèi)型的數(shù)據(jù)都比二進(jìn)制數(shù)據(jù)更容易處理)。

    在這些情況中,您只使用了適當(dāng)?shù)念^名,并將其傳遞給 XMLHttpRequest 對(duì)象的 getResponseHeader() 方法。因此要獲取響應(yīng)的長(zhǎng)度,只需要調(diào)用 request.getResponseHeader("Content-Length");。要獲取內(nèi)容類(lèi)型,請(qǐng)使用 request.getResponseHeader("Content-Type");

    在很多應(yīng)用程序中,生成 HEAD 請(qǐng)求并沒(méi)有增加任何功能,甚至可能會(huì)導(dǎo)致請(qǐng)求速度變慢(通過(guò)強(qiáng)制生成一個(gè) HEAD 請(qǐng)求來(lái)獲取有關(guān)響應(yīng)的數(shù)據(jù),然后在使用一個(gè) GET 或 POST 請(qǐng)求來(lái)真正獲取響應(yīng))。然而,在出現(xiàn)您不確定有關(guān)腳本或服務(wù)器端組件的情況時(shí),使用 HEAD 請(qǐng)求可以獲取一些基本的數(shù)據(jù),而不需要對(duì)響應(yīng)數(shù)據(jù)真正進(jìn)行處理,也不需要大量的帶寬來(lái)發(fā)送響應(yīng)。

    結(jié)束語(yǔ)

    對(duì)于很多 Ajax 和 Web 程序員來(lái)說(shuō),本文中介紹的內(nèi)容似乎是太高級(jí)了。生成 HEAD 請(qǐng)求的價(jià)值是什么呢?到底在什么情況下需要在 JavaScript 中顯式地處理重定向狀態(tài)代碼呢?這些都是很好的問(wèn)題;對(duì)于簡(jiǎn)單的應(yīng)用程序來(lái)說(shuō),答案是這些高級(jí)技術(shù)的價(jià)值并不是非常大。

    然而,Web 已經(jīng)不再是只需實(shí)現(xiàn)簡(jiǎn)單應(yīng)用程序的地方了;用戶已經(jīng)變得更加高級(jí),客戶期望能夠獲得更好的穩(wěn)定性、更高級(jí)的錯(cuò)誤報(bào)告,如果應(yīng)用程序有 1% 的時(shí)間停機(jī),那么經(jīng)理就可能會(huì)因此而被解雇。

    因此您的工作就不能僅僅局限于簡(jiǎn)單的應(yīng)用程序了,而是需要更深入理解 XMLHttpRequest

    • 如果您可以考慮各種就緒狀態(tài) —— 并且理解了這些就緒狀態(tài)在不同瀏覽器之間的區(qū)別 —— 就可以快速調(diào)試應(yīng)用程序了。您甚至可以基于就緒狀態(tài)而開(kāi)發(fā)一些創(chuàng)造性的功能,并向用戶和客戶回報(bào)請(qǐng)求的狀態(tài)。
    • 如果您要對(duì)狀態(tài)代碼進(jìn)行控制,就可以設(shè)置應(yīng)用程序來(lái)處理腳本錯(cuò)誤、非預(yù)期的響應(yīng)以及邊緣情況。結(jié)果是應(yīng)用程序在所有的時(shí)間都可以正常工作,而不僅僅是只能一切都正常的情況下才能運(yùn)行。
    • 增加這種生成 HEAD 請(qǐng)求的能力,檢查某個(gè) URL 是否存在,以及確認(rèn)某個(gè)文件是否被修改過(guò),這樣就可以確保用戶可以獲得有效的頁(yè)面,用戶所看到的信息都是最新的,(最重要的是)讓他們驚訝這個(gè)應(yīng)用程序是如何健壯和通用。

    本文的目的并非是要讓您的應(yīng)用程序顯得十分華麗,而是幫助您去掉黃色聚光燈后重點(diǎn)昭顯文字的美麗,或者外觀更像桌面一樣。盡管這些都是 Ajax 的功能(在后續(xù)幾篇文章中就會(huì)介紹),不過(guò)它們卻像是蛋糕表面的一層奶油。如果您可以使用 Ajax 來(lái)構(gòu)建一個(gè)堅(jiān)實(shí)的基礎(chǔ),讓?xiě)?yīng)用程序可以很好地處理錯(cuò)誤和問(wèn)題,用戶就會(huì)返回您的站點(diǎn)和應(yīng)用程序。在接下來(lái)的文章中,我們將添加這種直觀的技巧,這會(huì)讓客戶 興奮得發(fā)抖。(認(rèn)真地說(shuō),您一定不希望錯(cuò)過(guò)下一篇文章!)






    回頁(yè)首


    下載

    描述名字大小下載方法
    Example code for this article wa-ajaxintro3_ajax-xhr_adv.zip 183KB HTTP
    關(guān)于下載方法的信息


    參考資料

    學(xué)習(xí)

    posted on 2008-03-12 18:32 gembin 閱讀(728) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): AjaxWeb 2.0

    導(dǎo)航

    統(tǒng)計(jì)

    常用鏈接

    留言簿(6)

    隨筆分類(lèi)(440)

    隨筆檔案(378)

    文章檔案(6)

    新聞檔案(1)

    相冊(cè)

    收藏夾(9)

    Adobe

    Android

    AS3

    Blog-Links

    Build

    Design Pattern

    Eclipse

    Favorite Links

    Flickr

    Game Dev

    HBase

    Identity Management

    IT resources

    JEE

    Language

    OpenID

    OSGi

    SOA

    Version Control

    最新隨筆

    搜索

    積分與排名

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    free counters
    主站蜘蛛池模板: 日本一区二区三区日本免费| 亚洲成aⅴ人在线观看| 亚洲AV无码成人网站在线观看| 最近中文字幕国语免费完整| 亚洲人色婷婷成人网站在线观看| 黄色网址免费在线| 国产无遮挡裸体免费视频 | xx视频在线永久免费观看| 亚洲av永久无码制服河南实里| 9久久免费国产精品特黄| 亚洲精品第一国产综合境外资源| 亚洲av综合日韩| 日本高清免费aaaaa大片视频| 亚洲精品456人成在线| 免费看国产精品3a黄的视频| 亚洲va精品中文字幕| 99爱在线精品免费观看| 久久精品国产亚洲av麻豆小说 | 日本xxwwxxww在线视频免费 | 国产h视频在线观看网站免费| 91在线亚洲精品专区| 99免费在线观看视频| 一区二区三区亚洲| 67194国产精品免费观看| 久久精品国产精品亚洲毛片| 久久午夜羞羞影院免费观看| 亚洲精品在线观看视频| 性色午夜视频免费男人的天堂| 亚洲成人午夜在线| 最近中文字幕完整免费视频ww | 99视频在线观看免费| 亚洲人成人77777网站| 日韩免费高清播放器| 久久午夜夜伦鲁鲁片无码免费| 亚洲va久久久噜噜噜久久男同| 在线涩涩免费观看国产精品 | 国产乱辈通伦影片在线播放亚洲| 4hu四虎免费影院www| 国产精品久久久亚洲| 日韩免费无码视频一区二区三区| 久久亚洲日韩看片无码|