簡體   English   中英

在ASP.NET中運行的異常處理應用程序塊異常處理程序無法調用Response.End()

[英]Exception Handling Application Block Exception Handler running in ASP.NET cannot call Response.End()

使用.NET 3.5,ASP.NET,Enterprise Library 4.1異常處理和日志記錄塊,我編寫了一個自定義異常處理程序以顯示標准錯誤頁面,如下所示:

[ConfigurationElementType(typeof(CustomHandlerData))]
public class PageExceptionHandler : IExceptionHandler {

    public PageExceptionHandler(NameValueCollection ignore) {
    }

    public Exception HandleException(Exception ex, Guid handlingInstanceID) {

        HttpResponse response = HttpContext.Current.Response;
        response.Clear();
        response.ContentEncoding = Encoding.UTF8;
        response.ContentType = "text/html";
        response.Write(BuildErrorPage(ex, handlingInstanceID));
        response.Flush();
        //response.End(); // SOMETIMES DOES NOT WORK

        return ex;
    }
}

這是從如下的異常處理策略中調用的:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  </configSections>
  <exceptionHandling>
    <exceptionPolicies>
      <add name="Top Level">
        <exceptionTypes>
          <add type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="None" name="Exception">
            <exceptionHandlers>
              <add logCategory="General" eventId="0" severity="Error" title="Application Error"
                formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                priority="0" useDefaultLogger="false" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                name="Log Full Details" />
              <add type="PageExceptionHandler, Test, Culture=neutral, PublicKeyToken=null"
                name="Display Error Page" />
            </exceptionHandlers>
          </add>
        </exceptionTypes>
      </add>
    </exceptionPolicies>
  </exceptionHandling>
</configuration>

一切正常,除了將錯誤頁面合並到發生錯誤時顯示的任何標記中。 我認為將response.End()添加到異常處理程序中可以解決此問題。 它確實做到了,但是隨后我觀察到,有時ASP.NET在嘗試結束響應流時會引發ThreadAbortException 這是唯一無法捕獲和忽略的異常類型。 rr! 誰能告訴我:

  1. 在什么條件下,ASP.NET在嘗試結束響應流時會拋出ThreadAbortException
  2. 有什么可以替代response.End()的安全方法嗎?
  3. 如果不是,是否有一種方法可以在調用response.End()之前檢測響應流是否“可結束”?

編輯-更多上下文

此代碼的設計目標是使添加EHAB樣式的錯誤處理到Web應用程序盡可能簡單。 我有一個自定義IHttpModule ,必須將其添加到web.config文件中。 在模塊的Application_Error事件中,它調用ExceptionPolicy.HandleException 必須將異常處理策略配置為調用上述的PageExceptionHandler類。 整個過程僅涉及少量的XML配置,無需使用額外的文件,並且在使用的Web應用程序中無需使用任何額外的代碼行。

實際上,目前的代碼似乎可以正常工作。 但是,在某些情況下,需要在Web應用程序代碼中有一個明確的catch塊,該塊直接調用異常處理策略。 這種情況是無法正常工作的。 我想要一個可以在所有可能的調用方法下工作的解決方案。 我確實有一個印象,那就是如果不涉及EHAB,它會簡單得多,但是不幸的是,它在我們的所有代碼中都使用,並且提供了許多其他好處,我希望保留它。

編輯-測試結果

我創建了一個測試工具,以六種不同的方式來執行代碼:

  1. 直接寫入當前頁面的HttpResponse
  2. 構造一個異常對象並直接調用ExceptionPolicy.HandleException(ex, "Top Level")
  3. 拋出一個異常對象,對其進行捕獲,然后在catch塊中調用ExceptionPolicy.HandleException(ex, "Top Level")
  4. 拋出異常對象並讓Page_Error事件調用ExceptionPolicy.HandleException(ex, "Top Level")
  5. 引發異常對象,並讓Application_Error事件調用ExceptionPolicy.HandleException(ex, "Top Level")
  6. 拋出一個異常對象,並讓我的自定義IHttpModule類調用ExceptionPolicy.HandleException(ex, "Top Level")

編寫錯誤頁面標記后,每種方法的測試結果都沒有代碼可終止響應:

  • 方法1、2、3-錯誤頁標記與測試頁標記相結合。
  • 方法4、5、6-錯誤頁面標記替換了測試頁面標記(期望的結果)。

調用HttpContext.Current.ApplicationInstance.CompleteRequest時每種方法的測試結果:

  • 方法1、2、3-錯誤頁標記與測試頁標記相結合。
  • 方法4、5、6-錯誤頁面標記替換了測試頁面標記(期望的結果)。

調用HttpContext.Current.Response.End時每個方法的測試結果:

  • 方法1、5、6-錯誤頁面標記替換了測試頁面標記(期望的結果)。
  • 方法2、3、4-錯誤頁面標記替換了測試頁面標記,但是EHAB 兩次拋出ExceptionHandlingException

調用HttpContext.Current.Response.End時每個方法的測試結果,但包裝在try ... catch塊中:

  • 方法5、6-錯誤頁面標記替換了測試頁面標記(期望的結果)。
  • 方法1、3、4-錯誤頁標記替換了測試頁標記,但是ASP.NET拋出ThreadAbortException ,該異常由catch塊捕獲並吸收。
  • 方法2-錯誤頁面標記替換了測試頁面標記,但是我得到了ThreadAbortException 兩個ExceptionHandlingException

這是一種荒謬的行為。 :-(

看看ELMAH
關於ELMAH的文章和方法有很多,只是通過Google或必應搜索即可:-)

好處
+記錄幾乎所有內容
+電子郵件通知
+遠程查看錯誤

我建議使用它並重定向到默認錯誤頁面,如Ariel所述。

看一下這個微軟的解釋 它說它適用於ASP.Net 1.1,但我認為它仍適用於2.0。 基本上,您應該改為調用HttpContext.Current.ApplicationInstance.CompleteRequest方法。

另外,我不確定為什么您無法捕獲ThreadAbortException為什么您不能執行類似的操作

try {
    // code
} catch (ThreadAbortException)
{
    //do nothing
}

我處理ASP.net異常的方法始終是捕獲它,將其記錄下來,然后重定向到帶有一些標准錯誤消息的通用錯誤頁面(最終用戶將不在乎異常或堆棧跟蹤)。 您已經在處理程序之前將異常記錄到了策略中,所以也許您要做的就是將響應重定向到錯誤頁面,然后將BuildErrorPage邏輯移到該頁面。 您可以在querystring中傳遞handlingInstanceId。

嘗試在Global.asax中使用Application_Error來捕獲應用程序中任何頁面引發的任何未處理異常。

這可能會有所幫助: http : //msdn.microsoft.com/en-us/library/cyayh29d.aspx

特別是“您的清理代碼必須在catch子句或finally子句中,因為ThreadAbortException在系統中finally子句的末尾或在catch子句的末尾(如果沒有finally子句的情況)被系統拋出

您可以通過調用Thread .. ::。ResetAbort方法來防止系統拋出異常。 但是,僅當您自己的代碼導致ThreadAbortException時才應這樣做。

請注意,Response.End和Response.Redirect在某些情況下可能會引發ThreadAbortException。

經過大量測試,我發現只有兩種方法可以正常工作。

解決方案1

完全不更改當前架構。 全局異常處理程序已按預期工作。 如果出於某種原因在代碼中需要顯式的catch塊(例如,在輸入表單上呈現驗證錯誤),那么我將告訴開發人員顯式調用Response.End ,例如

try {
    // ...
} catch(Exception ex) {
    if(ExceptionPolicy.HandleException(ex, "Top Level")) {
        throw;
    }
    Response.End();
}

解決方案2

放棄嘗試覆蓋當前響應流並真正重定向到錯誤頁面的嘗試。 這對所有六個測試工具方法都可以正常工作。 下一個問題是找到一種方法,該方法需要對使用方項目進行最小的更改,並提取所有臟的細節。

步驟1-將占位符ErrorPage.ashx文件添加到項目中,該文件僅包含對標准網頁處理程序類的引用,而沒有任何代碼后綴。

<%@ WebHandler Class="WebApplicationErrorPage" %>

第2步-使用會話密鑰傳遞所有必需的數據,重定向到PageExceptionHandler類中的此頁面。

public Exception HandleException(Exception ex, Guid handlingInstanceID) {

    HttpResponse response = HttpContext.Current.Response;
    HttpSessionState session = HttpContext.Current.Session;

    session["ErrorPageText"] = BuildErrorPage(ex, handlingInstanceID);
    response.Redirect(errorPageUrl, false);

    return ex;
}

第3步-使WebApplicationErrorPage類成為僅寫入響應流的真正愚蠢的HTTP處理程序。

public class WebApplicationErrorPage : IHttpHandler, IReadOnlySessionState {

    public bool IsReusable {
        get {
            return true;
        }
    }

    public void ProcessRequest(HttpContext context) {

        response.Clear();
        response.ContentEncoding = Encoding.UTF8;
        response.ContentType = "text/html";
        response.Write((string) context.Session["ErrorPageText"]);
        response.Flush();
    }
}

結論

我認為我會堅持使用解決方案1,因為它不涉及斬波和更改現有體系結構(真正實現解決方案2的過程比上面的代碼片段要復雜得多)。 我將提交解決方案2以便在其他地方使用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM