I was having a problem in an MVC 3 app with the HandleErrorAttribute
hiding exceptions from ELMAH and also clashing with the <customErrors>
section in the Web.config (similar to this Stack Overflow question). TL;DR version: I ended up just removing the default registration of HandleErrorAttribute
and leaning on what’s specified in <customErrors>
.
Some details…
By default, a new MVC 3 project has a global filter registered in Global.asax that applies the HandleErrorAttribute
to each controller action. This attribute will essentially appy a try/catch around each controller action and render the Error.cshtml file (or .vbhtml if you’re using Razor with VB.NET, or .aspx if you’re using the WebForms view engine) in the Views/ControllerName or Views/Shared directory. A new MVC 3 project has a Error.cshtml file in the Views/Shared directory by default.
If custom errors are enabled (either via the mode attribute being set to “On” or “RemoteOnly” on the <customErrors>
element), then HandleErrorAttribute
will render the Error.cshtml view.
My first problem was that I was using ELMAH to log errors, and exceptions would be swallowed by HandleErrorAttribute
and not logged in ELMAH. I saw a few posts such as this one from Scott Hanselman about making a custom attribute to handle errors and log to ELMAH, but stay with me for a few minutes more…
To handle different kinds of custom errors, I removed the stock Error.cshtml from Views/Shared, made a new controller and set the following in the Web.config file:
<customErrors mode="On" defaultRedirect="~/Error/ServerError">
<error statusCode="404" redirect="~/Error/NotFound" />
<!-- Other status codes here -->
</customErrors>
However, if an exception was thrown with HandleErrorAttribute
and the <customErrors>
settings applied, I would first get another exception (logged in ELMAH):
The view ‘Error’ or its master was not found or no view engine supports the searched locations. The following locations were searched: ~/Views/ControllerName/Error.aspx ~/Views/ControllerName/Error.ascx ~/Views/Shared/Error.aspx ~/Views/Shared/Error.ascx ~/Views/ControllerName/Error.cshtml ~/Views/ControllerName/Error.vbhtml ~/Views/Shared/Error.cshtml ~/Views/Shared/Error.vbhtml
Then, the app would fall back to rendering Error/ServerError as I had directed it to in <customErrors>
. The real error was masked in ELMAH. This is because the HandleErrorAttribute
was looking for Shared/Error.cshtml, couldn’t find it, then threw up and fell back to what was specified in <customErrors>
.
(Note that 404 errors still passed through; it turns out HandleErrorAttribute
ignores all but 500 errors).
Why did I even need to use the HandleErrorAttribute
? Can’t I just lean on the <customErrors>
section in vanilla ASP.NET? I ended up taking the attribute’s registration out of Global.asax and doing just that. Errors were then properly picked up by Elmah, and I would see friendly error messages when enabled in production.