ExceptionContext.ExceptionHandled changes to true. Where is the exception being handled?

escist picture escist · May 15, 2012 · Viewed 9.3k times · Source

I'm using a global action filter to handle and log all exceptions.

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new ElmahHandleErrorAttribute());
        filters.Add(new HandleErrorAttribute());
    }

This is how the global action filter ElmahHandleErrorAttribute is defined - it overrides the OnException method.

public class ElmahHandleErrorAttribute : System.Web.Mvc.HandleErrorAttribute
{
    public override void OnException(ExceptionContext context)
    {
       //Is the exception handled already? context.ExceptionHandled seems to be true here
        if (!context.IsChildAction && (context.HttpContext.IsCustomErrorEnabled))
        {
            //Do other stuff stuff
            //Log to Elmah               
        }
    }
   ...
 }

I don't understand why the value of context.ExceptionHandled is true when the OnException method executes. How is this exception getting handled?

-EDIT- I have a customErrors section in the Web.Config. I have an ErrorController class, and actions called General and Http404.

<customErrors mode ="On" defaultRedirect="Error/General">
      <error statusCode="404" redirect="Error/Http404"/>
  </customErrors>

What I don't understand is, the controller action General is not executed (breakpoint is never hit), but the value of ExceptionContext.ExceptionHandled is set to true when the OnException method of ElmahHandleErrorAttribute starts executing.

Answer

Richard Fawcett picture Richard Fawcett · May 18, 2012

When an exception occurs, the order of the global filters executes in reverse order. This means that HandleErrorAttribute runs first.

You can view the code of HandleErrorAttribute here, but in short, it:

  1. Only executes if ExceptionHandled is false, and custom errors are enabled.
  2. Sets up a redirect to the error view, which by default is called Error.
  3. Sets ExceptionHandled to true.

As it's the first filter, then ExceptionHandled is false when it executes, causing it to set the view to Error and setting ExceptionHandled to true. So, then, when your own filter executes, that is why ExceptionHandled is already set to true. Note that if custom errors were disabled, then ExceptionHandled would still be false, as HandleErrorAttribute wouldn't have done its stuff. In this case, ELMAH will log the error anyway, as it's unhandled (yellow screen of death), so the test in your class is to prevent duplicate logging of the error.

Now, on to your other question about whey the General action isn't executed, the defaultRedirect is only used if the filters don't set some explicit redirect themselves, so it's actually ignored when an exception occurs inside an ActionMethod and you have the global filter HandleErrorAttribute registered. It would however, be called if you entered a URL that didn't exist, i.e. an error that doesn't occur from within an ActionMethod. Also, if you comment out the line to register the HandleErrorAttribute in Global.asax.cs, then you'll always get the General controller action executing.