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

1

10

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.

Contractile answered 15/5, 2012 at 9:6 Comment(4)
I can't see context.ExceptionHandled in your code, where is it?Startling
@Startling I removed the check for context.ExceptionHandled. It was earlier included in the statement: if (!context.IsChildAction && !context.ExceptionHandled && (context.HttpContext.IsCustomErrorEnabled))Contractile
Did you override the OnException method of the controller?Startling
@gdoron: No I override the OnException method of the MVC HandleErrorAttribute filter.Contractile
C
25

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.

Colp answered 18/5, 2012 at 10:21 Comment(5)
Thank you for your explanation. It really helped me. I want ElmahHandleErrorAttribute to be the first filter that executes. Would setting the order of this custom filter fix this? As per this comment (https://mcmap.net/q/590875/-handleerrorattribute-not-working) I should give a higher Order value to the filter I want to execute first.Contractile
Yes, I suspect you could play around with the Order property (but not sure how do do that on a global filter, have only done this when using it as an attribute). Simply reversing the order you add the filters in RegisterGlobalFilters should work ... when an error occurs, the one added last will run first. Not sure why you would want the ELMAH one to run first though, and if you really do, you can't check for ExceptionHandled as it will always be false in that case.Colp
I specified the orders when registering the filters. Now my custom filter ElmahHandleErrorAttribute is handling the error first. Regarding your question Not sure why you would want the ELMAH one to run first though, Do you think this is a bad idea? I'm doing this because I want to display custom error pages and check the error type and display a specific view.Contractile
Providing that the ELMAH view will always display a view, then yes, I think that's OK. However, if you leave the default HandleErrorAttribute to handle the fallback case, if it doesn't set a view either, then a YSOD occurs and you'll potentially end up with ELMAH logging the error twice (once in the global filter, and once when the exception isn't marked as handled). If you're setting views in your ELMAH filter, then maybe you should get rid of the HandleErrorAttribute altogether?Colp
I guess I should get rid of HandleErrorAttribute. The custom ElmahHandleErrorAttribute will display a view. Since I have defined Application_Error() in global.asax to handle 404 and other unhandled exceptions, I don't think a YSOD would be displayed.Contractile

© 2022 - 2024 — McMap. All rights reserved.