C# – Exception Handling Application Block Exception Handler running in ASP.NET cannot call Response.End()

asp.netcenterprise-librarynet

Using .NET 3.5, ASP.NET, Enterprise Library 4.1 Exception Handling and Logging blocks, I wrote a custom exception handler to display a standard error page, as follows:

[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;
    }
}

This is called from an exception handling policy like this:

<?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>

It all works fine, except that the error page was being incorporated into whatever markup was displayed at the time the error occurred. I thought that adding response.End() into the exception handler would solve this problem. It did, but I then observed that, sometimes, ASP.NET throws a ThreadAbortException while trying to end the response stream. This is the only exception type that cannot be caught and ignored. Grrr! Can anyone tell me:

  1. Under what conditions ASP.NET would throw a ThreadAbortException when trying to end the response stream?
  2. Is there a safer alternative to response.End() that I could use?
  3. If not, is there a way of detecting whether the response stream is "end-able" before calling response.End()?

EDIT – More Context

The design goal for this code is to make it as simple as possible to add EHAB-style error handling to a web app. I have a custom IHttpModule that must be added to the web.config file. In the module's Application_Error event it calls ExceptionPolicy.HandleException. The exception handling policy must be configured to call the PageExceptionHandler class described above. This whole procedure involves only a small amount of XML config, no extra files, and no extra lines of code in the consuming web app.

In fact, the code as it stands seems to work fine. However, in some situations it is desirable to have an explicit catch block in the web app code that calls the exception handling policy directly. This sort of scenario is what does not work properly. I would like a single solution that works under all possible methods of calling it. I do get the impression that it would be a lot simpler if the EHAB was not involved, but unfortunately it is used throughout all our code, and provides so many other benefits that I would prefer to keep it in.

EDIT – Testing Results

I created a test harness to exercise the code in six different ways:

  1. Writing to the current page's HttpResponse directly.
  2. Constructing an exception object and calling ExceptionPolicy.HandleException(ex, "Top Level") directly.
  3. Throwing an exception object, catching it, and calling ExceptionPolicy.HandleException(ex, "Top Level") in the catch block.
  4. Throwing an exception object and letting the Page_Error event call ExceptionPolicy.HandleException(ex, "Top Level").
  5. Throwing an exception object and letting the Application_Error event call ExceptionPolicy.HandleException(ex, "Top Level").
  6. Throwing an exception object and letting my custom IHttpModule class call ExceptionPolicy.HandleException(ex, "Top Level").

Test results for each method with no code to terminate the response after writing the error page markup:

  • Methods 1, 2, 3 – Error page markup combined with the test page markup.
  • Methods 4, 5, 6 – Error page markup replaced the test page markup (desired result).

Test results for each method when HttpContext.Current.ApplicationInstance.CompleteRequest was called:

  • Methods 1, 2, 3 – Error page markup combined with the test page markup.
  • Methods 4, 5, 6 – Error page markup replaced the test page markup (desired result).

Test results for each method when HttpContext.Current.Response.End was called:

  • Methods 1, 5, 6 – Error page markup replaced the test page markup (desired result).
  • Methods 2, 3, 4 – Error page markup replaced the test page markup, but the EHAB throws a ExceptionHandlingException twice.

Test results for each method when HttpContext.Current.Response.End was called, but wrapped in a trycatch block:

  • Methods 5, 6 – Error page markup replaced the test page markup (desired result).
  • Methods 1, 3, 4 – Error page markup replaced the test page markup, but ASP.NET throws a ThreadAbortException which is caught and absorbed by the catch block.
  • Method 2 – Error page markup replaced the test page markup, but I get a
    ThreadAbortException and also two ExceptionHandlingExceptions.

It's a ridiculous set of behaviours. 🙁

Best Answer

Have a look at ELMAH.
There are lots of articles and how-to's on ELMAH, just google or bing for it :-)

Advantages
+ It logs almost everything
+ Email notification
+ Remote viewing of errors

I would suggest to use that and redirect to a default error page, as Ariel mentioned.