4.3.4 Server對象的錯誤處理
ASP沒有錯誤處理機制一直受到批評。
在VBScript中,有一個On Error Resume Next語句,它使腳本解釋器忽略運行期錯誤並繼續腳本代碼的執行。接著該腳本可以檢查Err.Number屬性的值,判別是否出現了錯誤。如果出現錯誤,返回一個非零值。在ASP 3.0中,也可以使用On Error Goto 0“轉回到”缺省的錯誤處理。在ASP 2.0中實際也進行這種處理,但是沒有相應文檔說明。
在JScript中,有一個新的錯誤處理功能:C語言風格的try和catch語句。然而所有的這些錯誤處理技術都不是由ASP或IIS實現的,而是由ASP使用的腳本引擎實現的。
第7章專門討論腳本和腳本引擎涉及到的調試和錯誤處理技術。
同時,ASP和IIS的開發小組已經增加了一個新的功能,用於在ASP網頁中進行錯誤處理。這分為兩個部分:IIS錯誤頁面的配置及使用ASP的一個新的方法和對象。
1. Server對象的GetLastError方法
在ASP 3.0中,Server對象有一個名為GetLastError的新方法。與VBScript的Err對象不同,不能為查看是否出現了錯誤而隨時調用該方法,只能在一個ASP定制的錯誤網頁中使用。如果像對Err對象進行操作那樣,通過關閉缺省的錯誤處理(用On Error Resume Next語句)來使用,則GetLastError方法不能訪問錯誤的詳細數據。
GetLastError方法要做的事情是提供更多的有關錯誤源和錯誤原因的信息。GetLastError方法創建並返回一個對象的引用,該對象是一個名為ASPError的新對象。這個對象具有一系列的屬性,這些屬性返回有關在GetLastError方法調用之前出現的最新錯誤的信息。
2. ASPError對象的屬性
ASPError對象提供了九個屬性說明所出現的錯誤的性質和錯誤源,並返回引發錯誤的實際代碼,其屬性及說明如表4-4所示:
表4-4 ASPError對象的屬性及說明
屬 性
說 明
ASPCode
整型。由ASP/IIS產生的錯誤號,例如0x800A009
ASPDescription
字符串型。如果這個錯誤是與ASP相關的錯誤,這個屬性是錯誤的詳細說明
Category
字符串型。錯誤源,即ASP內部腳本語言、或一個對象
Column
整型。產生錯誤的文件中的字符位置
Description
字符串型。錯誤的簡短說明
File
字符串型。錯誤出現時正在處理的文件的名稱
Line
整型。產生錯誤的文件中的行號
Number
整型。一個標准的COM錯誤代碼
Source
字符串型。引發錯誤的行的實際代碼
3. 配置“單個網頁”錯誤處理
在IIS中“不可思議”地出現一個錯誤(例如404 Not Found)時,頁面看起來像是從服務器返回給客戶端的一個錯誤信息頁面,但實際上並不是這樣。它們是普通的Html網頁,在對一個錯誤進行響應時被下載並且發送給客戶端。這些網頁通常稱為定制的錯誤網頁(custom error page)。
然而,錯誤網頁作為IIS的缺省安裝部分,可根據要求定制。事實上,也可以在IIS的早期版本中建立定制的錯誤網頁。
在IIS 4.0中,可以為每種不同類型的HTTP協議或服務器錯誤指定一個定制的錯誤網頁,為服務器上任意的Web網站中的每個目錄建立一個定制的錯誤信息網頁。
(1) IIS缺省的錯誤網頁
由IIS提供的缺省錯誤頁面放在Web服務器的WinNT\Help目錄中。在Windows 2000中的IIS 5.0的環境下,該頁面放在WinNT\Help\IIShelp\common目錄下,如圖4-17所示:
圖4-17 缺省的錯誤頁面位置
可在浏覽器中打開這些文件查看結果,或者在文本編輯器中查看Html源程序和腳本代碼。當一個404錯誤出現時,使用的頁面是404b.htm,這個文件包含一個客戶端腳本代碼部分,它獲得當前文檔的URL(從document對象的url屬性中檢索)並在該頁面中顯示:
<tr>
<td width="400" colspan="2"> <font >您正在搜索的網頁可能已經刪除、更名或暫時不可用。</font></td>
</tr>
<tr>
<td width="400" colspan="2"> <font >
<hr color="#C0C0C0" noshade>
<p>請嘗試下列操作:</p>
<ul>
<li>如果您在“地址”欄中鍵入了網頁地址,請檢查其拼寫是否正確。<br>
</li>
<li>打開 <script>
<!--
if (!((window.navigator.userAgent.indexOf("MSIE") > 0) && (window.navigator.appVersion.charAt(0) == "2")))
{
Homepage();
}
//-->
</script> 主頁,尋找指向所需信息的鏈接。</li>
…
<script>
function Homepage(){
<!--
DocURL = document.URL;
PRotocolIndex=DocURL.indexOf("://",4);
serverIndex=DocURL.indexOf("/",protocolIndex + 3);
BeginURL=DocURL.indexOf("#",1) + 1;
urlresult=DocURL.substring(BeginURL,serverIndex);
displayresult=DocURL.substring(protocolIndex + 3 ,serverIndex);
document.write('<A HREF="' + urlresult + '">' + displayresult + "</a>");
}
//-->
</script>
這會產生你經常看到的頁面,如圖4-18所示:
圖4-18 產生404錯誤時的頁面
(2) IIS中錯誤網頁的映射
當IIS檢測到一個錯誤時,會把相應的錯誤頁面傳送給客戶端。如何判別應該向客戶端發送那一個頁面?很明顯,網頁的名字應具有解決這個問題的一些信息,但事實上文件名是不重要的。錯誤和錯誤網頁文件之間的映射關系是在每個目錄的propertIEs對話框的Custom Errors選項卡中決定的。
在Internet Services Manager中,在想編輯映射關系的目錄上單擊右鍵,並選擇PropertIEs。如果對示例文件進行設置,在Chapter04目錄中選擇server子目錄,如圖4-19所示:
圖4-19 設置屬性時的頁面屏幕
PropertIEs對話框的Custom Errors選項卡在IIS安裝時(除非已經進行過修改)設置了缺省映射關系的列表,如圖4-20所示:
圖4-20 映射關系的列表
靠近該列表的底部是HTTP錯誤500:100的一個條目。類型500錯誤是由ASP產生的,可以從中看出一些錯誤已經與錯誤網頁建立了映射關系。這些錯誤都是一般性的錯誤,比如“Invalid application”、“Server Shutting Down”等等。然而,如果ASP載入包含語法錯誤的頁面,或者出現一個運行期錯誤,則出現500:100錯誤頁面。在列表中顯示的缺省映射關系表明,在這個目錄中的一個文件出現上述錯誤時,將執行500-100.ASP頁面。
當一個ASP錯誤出現時,我們所看到的信息不再是一個普通的Web網頁,而是一個ASP Web網頁(也就是說它具有文件擴展名.ASP)。也可以根據需要編輯該映射關系來指向另一個頁面。
(3) 指定一個定制的錯誤網頁
單擊Custom Errors選項中的Edit Properties按鈕,打開Error Mapping PropertIEs對話框。在Message Type下拉列表中選擇URL,鍵入自己的定制錯誤網頁的完整虛擬路徑,如圖4-21所示:
圖4-21 指定錯誤頁面的虛擬路徑的屏幕
在圖4-21中給出的值指向我們創建的與示例網頁一起使用的一個定制錯誤網頁。根據你安裝示例文件的具體位置,可能要使用不同的路徑。
現在無論何時出現一個500:100類型的錯誤,將打開我們的定制錯誤頁面。Message Type的其他兩個選項是:
· Default(缺省):可以簡單地輸入一個短的文本信息,而不是指定一個發送給客戶端的頁面。
· File(文件):指定一個HTTP錯誤網頁的物理路徑。
在選擇File選項時,指定的網頁由IIS載入,載入的方式與在Windows Explorer中雙擊要載入的文件時的方式相同。這意味著ASP網頁不能使用這個選項,因為在這種情況下不會執行其中的任何腳本。
4. 使用GetLastError方法和ASPError對象
配置好IIS後,在編輯了錯誤映射屬性的目錄內的任一頁面上出現一個與ASP相關的錯誤時,都將載入定制錯誤頁面。實際上,現在已經設置了一個正常的腳本錯誤陷阱,因為在這個目錄內的任何一個網頁上的ASP運行期錯誤都將觸發定制錯誤頁面。
事實上在內部IIS通過Server.Transfer方法進行這種操作,這意味著能夠訪問正在運行的原網頁的全部環境。可以在腳本環境中獲取信息,這樣可以根據所出現的錯誤決定要做些什麼。在此基礎上,可以在定制的錯誤網頁中檢索ASPError對象,找到引起載入頁面出錯的錯誤的所有信息。
在IIS 4.0中,編輯錯誤映射屬性要做一些類似的工作。但是只有一般的500錯誤(“Internal Server Error”)在映射中是可用的。另外,當定制錯誤網頁載入時,不會傳送網頁的環境,除了提供一個非特定的錯誤信息外,做其他任何工作都是比較困難的。
在以前例子中已經使用過ASP Server Object示例頁面,在其中可以看到ASPError對象的詳細情況。單擊Server.GetLastError()對應的按鈕,如圖4-22所示:
圖4-22 查看ASPError對象的詳細屏幕
這個操作會重新載入該網頁,其中的ASP腳本查看點擊的是哪個按鈕。如果是Server.GetLastError()對應的名為cmdGetError的按鈕,將執行一些示例代碼,這些代碼將會產生一個運行期腳本錯誤。
…
If Len(Request.Form(“cmdGetError”)) Then
Dim arrThis(3)
ArrThis(4) = “Causes an error”
End If
…
因為已對這個目錄設置了錯誤網頁映射,即配置為裝入定制錯誤頁面,所以當錯誤出現時,就打開這個頁面(通過Server.Transfer方法在後台不可見地工作),見圖4-23所示:
圖4-23 定制的錯誤網頁
(1) 示例錯誤網頁代碼的功能
定制錯誤網頁顯示ASPError對象屬性的所有值,並通過使用Response.Status方法,把一個HTTP報頭狀態消息返回給客戶端,指明出現了一個錯誤。接著使用GetLastError方法獲取對ASPError對象的一個引用,因此可以訪問錯誤的詳細數據:
…
<%
Response.Status = "500 Internal Server Error"
Set objASPError = Server.GetLastError()
%>
Currently executing the page: <B>show_error.ASP</B><P>
<B>Error Details:</B><BR>
ASPError.ASPCode = <% = objASPError.ASPCode %><BR>
ASPError.Number = <% = objASPError.Number %> (0x<% = Hex(objASPError.Number) %>)<BR>
ASPError.Source = <% = Server.HtmlEncode(objASPError.Source) %><BR>
ASPError.Category = <% = objASPError.Category %><BR>
ASPError.File = <% = objASPError.File %><BR>
ASPError.Line = <% = objASPError.Line %><BR>
ASPError.Column = <% = objASPError.Column %><BR>
ASPError.Description = <% = objASPError.Description %><BR>
ASPError.ASPDescription = <% = objASPError.ASPDescription %>
<FORM ACTION="<% = Request.ServerVariables("HTTP_REFERER") %>" METHOD="POST">
<INPUT TYPE="SUBMIT" NAME="cmdOK" VALUE=" ">
Return to the previous page<P>
</FORM>
值得注意的一點是,如果一個腳本或ASP錯誤出現在定制錯誤網頁中,IIS將僅僅返回一個與錯誤代碼500:100對應的一般性消息。這可能是腳本引擎自己的錯誤消息,或者只是相當簡單的消息:“Internal Server Error”。不會再次重新載入定制的錯誤網頁。
包含錯誤的網頁的全部環境將傳送給定制錯誤網頁。也就是說,可以使用存儲在任何ASP內部對象集合或屬性中的值。例如,如果檢索來自Request.ServerVariables集合的HTTP_REFERER值,它將反映調用原網頁的網頁(即在錯誤出現之前的網頁)的URL。在服務器把執行轉到錯誤網頁時,這個值不會發生變化,並且它將不包含當錯誤發生時正在執行的網頁的URL。
同樣,SCRIPT_NAME值將是包含該錯誤的網頁的名字,而不是錯誤網頁的URL。在一個錯誤網頁已經裝入時,通過檢查浏覽器地址欄中的URL,可以對此進行確認。但是在原網頁的腳本變量中存儲的值,在定制的錯誤網頁中都是不可用的。
如果原ASP網頁正在一個事務內運行,即在網頁的最前面包含有一個<% @TRANSACTION=”…” %>指令,也應該確定是否需要在網頁中采取一些方法,以退出該事務。例如可以調用內置ObjectContext對象的SetAbort方法:
objectContext.SetAbort ‘Fail the transaction if an ASP error occurs
在本書的後面將介紹與事務的相關全部內容。
(2) 使用ASPError對象的屬性
關於使用ASPError對象的屬性,有以下幾點值得注意的:
· 即使沒有出現錯誤,Number屬性應該一直有一個值。如果ASP網頁調用GetLastError方法時沒有錯誤出現,該屬性的值是0。通常情況下,對ASP腳本的運行期錯誤,Number屬性返回十六進制的值“0x800A0000”,加上標准的腳本引擎錯誤代碼。例如,前面的例子對“Subscript out of Range”錯誤的返回值為“0x800A0009”,因為VBScript對該類型錯誤的錯誤代碼是“9”。
· 當出現已經過一個錯誤時,Category和Description屬性將一直有一個值。
· APSCode屬性的值由IIS產生,對大多數腳本錯誤將為空。更多情況下,涉及外部組件使用出錯時有相應的值。
· ASPDescription屬性的值由ASP預處理程序產生,而不是由當前正在使用的腳本引擎產生的,並且對大多數腳本錯誤而言將是空的。更多情況下,對諸如對ASP內置對象調用無效的方法的錯誤有相應的值。
· File、Source、Line和column屬性僅在錯誤出現時,並且在錯誤的詳細數據是可用的情況下才能進行設置。對一個運行期錯誤,File和Line屬性通常是有效的,但是column屬性經常返回-1。當錯誤是一個阻止頁面被ASP處理的語法錯誤,才返回Source屬性。一般在這些情況下,Line和Column屬性是有效的。如果把Source屬性的值寫到頁面,明智的辦法是先將該值傳給HTMLEncode,以防在其含有非法的HTML字符。在本章的後面將詳細地討論HtmlEncode方法。