?? 掌握 ajax,第 3 部分 ajax 中的高級請求和響應.htm
字號:
的響應文本</B></A><BR><IMG height=334 alt="就緒狀態為 3 的響應文本"
src="掌握 Ajax,第 3 部分 Ajax 中的高級請求和響應.files/ready-state_3.jpg"
width=500> <BR>
<P>您會看到就緒狀態為 3
的響應在每個腳本、每個服務器甚至每個瀏覽器上都是不一樣的。不過,這在調試應用程序中依然是非常有用的。</P>
<P><A name=N102C5><SPAN class=smalltitle>獲取安全數據</SPAN></A></P>
<P>所有的文檔和規范都強調,只有在就緒狀態為 4 時數據才可以安全使用。相信我,當就緒狀態為 3 時,您很少能找到無法從
<CODE>responseText</CODE> 屬性獲取數據的情況。然而,在應用程序中將自己的邏輯依賴于就緒狀態 3
可不是什么好主意 —— 一旦您編寫了依賴于就緒狀態 3 的完整數據的的代碼,幾乎就要自己來負責當時的數據不完整問題了。</P>
<P>比較好的做法是向用戶提供一些反饋,說明在處于就緒狀態 3 時,很快就會有響應了。盡管使用 <CODE>alert()</CODE>
之類的函數顯然不是什么好主意 —— 使用 Ajax 然后使用一個警告對話框來阻塞用戶顯然是錯誤的 ——
不過您可以在就緒狀態發生變化時更新表單或頁面中的域。例如,對于就緒狀態 1 來說要將進度指示器的寬度設置為 25%,對于就緒狀態 2
來說要將進度指示器的寬度設置為 50%,對于就緒狀態 3 來說要將進度指示器的寬度設置為 75%,當就緒狀態為 4
時將進度指示器的寬度設置為 100%(完成)。</P>
<P>當然,正如您已經看到的一樣,這種方法非常聰明,但它是依賴于瀏覽器的。在 Opera 上,您永遠都不會看到前兩個就緒狀態,而在
Safari 上則沒有第一個(1)。由于這個原因,我將這段代碼留作練習,而沒有在本文中包括進來。</P>
<P>現在應該來看一下狀態代碼了。</P>
<P><A name=N102DF><SPAN class=atitle>深入了解 HTTP 狀態代碼</SPAN></A></P>
<P>有了就緒狀態和您在 Ajax 編程技術中學習到的服務器的響應,您就可以為 Ajax 應用程序添加另外一級復雜性了 —— 這要使用
HTTP 狀態代碼。這些代碼對于 Ajax 來說并沒有什么新鮮。從 Web 出現以來,它們就已經存在了。在 Web
瀏覽器中您可能已經看到過幾個狀態代碼:</P>
<UL>
<LI><B>401</B>:未經授權
<LI><B>403</B>:禁止
<LI><B>404</B>:沒找到 </LI></UL>
<P>您可以找到更多的狀態代碼(完整清單請參見 <A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#resources">參考資料</A>)。要為
Ajax 應用程序另外添加一層控制和響應(以及更為健壯的錯誤處理)機制,您需要適當地查看請求和響應中的狀態代碼。</P>
<P><A name=N10302><SPAN class=smalltitle>200:一切正常</SPAN></A></P>
<P>在很多 Ajax 應用程序中,您將看到一個回調函數,它負責檢查就緒狀態,然后繼續利用從服務器響應中返回的數據,如 <A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#listing6">清單
6</A> 所示。</P><BR><BR><A name=listing6><B>清單 6.
忽略狀態代碼的回調函數</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>
function updatePage() {
<SPAN class=boldcode>if (request.readyState == 4) {</SPAN>
var response = request.responseText.split("|");
document.getElementById("order").value = response[0];
document.getElementById("address").innerHTML =
response[1].replace(/\n/g, "<br />");
}
}
</PRE></TD></TR></TBODY></TABLE><BR>
<P>這對于 Ajax 編程來說證明是一種短視而錯誤的方法。如果腳本需要認證,而請求卻沒有提供有效的證書,那么服務器就會返回諸如 403
或 401 之類的錯誤代碼。然而,由于服務器對請求進行了應答,因此就緒狀態就被設置為
4(即使應答并不是請求所期望的也是如此)。最終,用戶沒有獲得有效數據,當 JavaScript
試圖使用不存在的服務器數據時就可能會出現嚴重的錯誤。</P>
<P>它花費了最小的努力來確保服務器不但完成了一個請求,而且還返回了一個 “一切良好” 的狀態代碼。這個代碼是 "200",它是通過
<CODE>XMLHttpRequest</CODE> 對象的 <CODE>status</CODE>
屬性來報告的。為了確保服務器不但完成了一個請求,而且還報告了一個 OK 狀態,請在您的回調函數中添加另外一個檢查功能,如 <A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#listing7">清單
7</A> 所示。</P><BR><BR><A name=listing7><B>清單 7. 檢查有效狀態代碼</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>
function updatePage() {
if (request.readyState == 4) {
<SPAN class=boldcode> if (request.status == 200) {</SPAN>
var response = request.responseText.split("|");
document.getElementById("order").value = response[0];
document.getElementById("address").innerHTML =
response[1].replace(/\n/g, "<br />");
<SPAN class=boldcode> } else
alert("status is " + request.status);</SPAN>
}
}
</PRE></TD></TR></TBODY></TABLE><BR>
<P>通過添加這幾行代碼,您就可以確認是否存在問題,用戶會看到一個有用的錯誤消息,而不僅僅是看到一個由斷章取義的數據所構成的頁面,而沒有任何解釋。</P>
<P><A name=N10343><SPAN class=smalltitle>重定向和重新路由</SPAN></A></P>
<P>在深入介紹有關錯誤的內容之前,我們有必要來討論一下有關一個在使用 Ajax 時 <I>并不需要</I> 關心的問題 ——
重定向。在 HTTP 狀態代碼中,這是 300 系列的狀態代碼,包括:</P>
<UL>
<LI><B>301</B>:永久移動
<LI><B>302</B>:找到(請求被重新定向到另外一個 URL/URI 上)
<LI><B>305</B>:使用代理(請求必須使用一個代理來訪問所請求的資源) </LI></UL>
<P>Ajax 程序員可能并不太關心有關重定向的問題,這是由于兩方面的原因:</P>
<UL>
<LI>首先,Ajax 應用程序通常都是為一個特定的服務器端腳本、servlet
或應用程序而編寫的。對于那些您看不到就消失了的組件來說,Ajax
程序員就不太清楚了。因此有時您會知道資源已經移動了(因為您移動了它,或者通過某種手段移動了它),接下來要修改請求中的
URL,并且不會再碰到這種結果了。
<LI>更為重要的一個原因是:Ajax 應用程序和請求都是封裝在沙盒中的。這就意味著提供生成 Ajax 請求的 Web
頁面的域必須是對這些請求進行響應的域。因此 ebay.com 所提供的 Web 頁面就不能對一個在 amazon.com
上運行的腳本生成一個 Ajax 風格的請求;在 ibm.com 上的 Ajax 應用程序也無法對在 netbeans.org
上運行的 servlets 發出請求。 </LI></UL>
<P>結果是您的請求無法重定向到其他服務器上,而不會產生安全性錯誤。在這些情況中,您根本就不會得到狀態代碼。通常在調試控制臺中都會產生一個
JavaScript 錯誤。因此,在對狀態代碼進行充分的考慮之后,您就可以完全忽略重定向代碼的問題了。</P>
<TABLE cellSpacing=0 cellPadding=0 width="50%" align=right
border=0><TBODY>
<TR>
<TD width=10><IMG height=1 alt=""
src="掌握 Ajax,第 3 部分 Ajax 中的高級請求和響應.files/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD bgColor=#eeeeee><A
name=N10374><B>邊界情況和困難情況</B></A><BR>
<P>看到現在,一些新手程序員就可能會這究竟是要討論什么內容。有一點事實大家需要知道:只有不到 5% 的
Ajax 請求需要使用諸如 2、3 之類的就緒狀態和諸如 403 之類的狀態代碼(實際上,這個比率可能更接近于
1% 甚至更少)。這些情況非常重要,稱為 <I>邊界情況(edge case)</I> ——
它們只會在一些非常特殊的情況下發生,其中遇到的都是最奇特的問題。雖然這些情況并不普遍,但是這些邊界情況卻占據了大部分用戶所碰到的問題的
80%!</P>
<P>對于典型的用戶來說,應用程序 100
次都是正常工作的這個事實通常都會被忘記,然而應用程序只要一次出錯就會被他們清楚地記住。如果您可以很好地處理邊界情況(或困難情況),就可以為再次訪問站點的用戶提供滿意的回報。</P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P><A name=N10383><SPAN class=smalltitle>錯誤</SPAN></A></P>
<P>一旦接收到狀態代碼 200 并且意識到可以很大程度上忽略 300 系列的狀態代碼之后,所需要擔心的唯一一組代碼就是 400
系列的代碼了,這說明了不同類型的錯誤。回頭再來看一下 <A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#listing7">清單
7</A>,并注意在對錯誤進行處理時,只將少數常見的錯誤消息輸出給用戶了。盡管這是朝正確方向前進的一步,但是要告訴從事應用程序開發的用戶和程序員究竟發生了什么問題,這些消息仍然是沒有太大用處的。</P>
<P>首先,我們要添加對找不到的頁的支持。實際上這在大部分產品系統中都不應該出現,但是在測試腳本位置發生變化或程序員輸入了錯誤的 URL
時,這種情況并不罕見。如果您可以自然地報告 404
錯誤,就可以為那些困擾不堪的用戶和程序員提供更多幫助。例如,如果服務器上的一個腳本被刪除了,我們就可以使用 <A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#listing7">清單
7</A> 中的代碼,這樣用戶就會看到一個如 <A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#fig5">圖
5</A> 所示的非描述性錯誤。</P><BR><BR><A name=fig5><B>圖 5.
常見錯誤處理</B></A><BR><IMG height=376 alt=常見錯誤處理
src="掌握 Ajax,第 3 部分 Ajax 中的高級請求和響應.files/generic_error.jpg"
width=500> <BR>
<P>用戶無法判斷問題究竟是認證問題、沒找到腳本(此處就是這種情況)、用戶錯誤還是代碼中有些地方產生了問題。添加一些簡單的代碼可以讓這個錯誤更加具體。請參照
<A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#listing8">清單
8</A>,它負責處理沒找到的腳本或認證發生錯誤的情況,在出現這些錯誤時都會給出具體的消息。</P><BR><BR><A
name=listing8><B>清單 8. 檢查有效狀態代碼</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>
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 />");
<SPAN class=boldcode> } else if (request.status == 404) {
alert ("Requested URL is not found.");
} else if (request.status == 403) {
alert("Access denied.");</SPAN>
} else
alert("status is " + request.status);
}
}
</PRE></TD></TR></TBODY></TABLE><BR>
<P>雖然這依然相當簡單,但是它的確多提供了一些有用的信息。<A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#fig6">圖
6</A> 給出了與 <A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#fig5">圖
5</A> 相同的錯誤,但是這一次錯誤處理代碼向用戶或程序員更好地說明了究竟發生了什么。</P><BR><BR><A
name=fig6><B>圖 6. 特殊錯誤處理</B></A><BR><IMG height=379 alt=特殊錯誤處理
src="掌握 Ajax,第 3 部分 Ajax 中的高級請求和響應.files/specific_error.jpg"
width=500> <BR>
<P>在我們自己的應用程序中,可以考慮在發生認證失敗的情況時清除用戶名和密碼,并向屏幕上添加一條錯誤消息。我們可以使用類似的方法來更好地處理找不到腳本或其他
400 類型的錯誤(例如 405 表示不允許使用諸如發送 HEAD 請求之類不可接受的請求方法,而 407
則表示需要進行代理認證)。然而不管采用哪種選擇,都需要從對服務器上返回的狀態代碼開始入手進行處理。</P>
<P><A name=N103DC><SPAN class=atitle>其他請求類型</SPAN></A></P>
<P>如果您真希望控制 <CODE>XMLHttpRequest</CODE> 對象,可以考慮最后實現這種功能 —— 將 HEAD
請求添加到指令中。在前兩篇文章中,我們已經介紹了如何生成 GET 請求;在馬上就要發表的一篇文章中,您會學習有關使用 POST
請求將數據發送到服務器上的知識。不過本著增強錯誤處理和信息搜集的精神,您應該學習如何生成 HEAD 請求。</P>
<P><A name=N103EA><SPAN class=smalltitle>生成請求</SPAN></A></P>
<P>實際上生成 HEAD 請求非常簡單;您可以使用 "HEAD"(而不是 "GET" 或 "POST")作為第一個參數來調用
<CODE>open()</CODE> 方法,如 <A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#listing9">清單
9</A> 所示。</P><BR><BR><A name=listing9><B>清單 9. 使用 Ajax 生成一個 HEAD
請求</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -