Mark Gilbert's Blog

Science and technology, served light and fluffy.

You can’t tell me what the error is because there was an error? ASP.NET MVC 2 and ELMAH

I just started a new project this week – a chartreuse-field project.  That is, it’s a greenfield project where we’re scrapping the existing site and replacing it with a new one, but with some legacy pieces being brought forward.  Why not call it a “brownfield project with heavy maintenance”?  Because we’re moving away from ASP.NET WebForms and going with the ASP.NET MVC 2 framework.  (The client is running their systems on .NET 3.5, which means we can’t go with the newer MVC 3 framework yet.  Sigh.)

While I had specific reasons for pushing the site in this direction, my decision to go this route was seriously called into question almost right out of the gate.  I was trying to configure ELMAH to work with MVC, and it was bothering me how difficult it was becoming.  Honestly, I think most of the difficulty was getting used to the MVC mindset again.  My first and last only experience with ASP.NET MVC was building the Microsoft Developers of Southwest Michigan user’s group site, http://DevMI.com, but that was a year and a half ago.

The core issue was getting the site to render my custom “Error” view when an unhandled exception occurred.  I was able to get the view to render by browsing to ~/Error.aspx/Unknown, so I used that in my web.config:

<customErrors mode=”On” defaultRedirect=”~/Error.aspx/Unknown”>
<error statusCode=”404″ redirect=”~/Error.aspx/NotFound”/>
</customErrors>

I then tried to test it by throwing a dummy exception in the home page’s controller.  The generic 500 page came up, not my custom one.

Grrr.

I spent a couple of hours researching this before I came across these two posts:

http://devstuffs.wordpress.com/2010/12/12/how-to-use-customerrors-in-asp-net-mvc-2/

http://www.hanselman.com/blog/ELMAHErrorLoggingModulesAndHandlersForASPNETAndMVCToo.aspx

Both of these posts mention the HandleError attribute that can be applied to both a controller class and the methods within.  What seemed to be happening was that this was preventing my custom error page (as defined in the web.config) from rendering.

I had started with the basic out-of-the-box MVC 2 template site, and then started trimming back what I didn’t need.  After reading these articles, I took a look at the HomeController class, and sure enough the HandleError attribute was applied.  I removed it and hit the home page again.  My custom “Error” view finally rendered.

Now that that was working, I dropped the other ELMAH configuration pieces in place in the web.config.  These mostly worked as they had in the past – I was able to see the custom error page, ELMAH was recording the exception on the file system, and it was generating an email to me with the exception – great!  We’re making progress!  There was just one more piece to my standard ELMAH implementation – the error report page.

With every site that I use ELMAH on, I configure the /errors/report.axd virtual page to show the list of the ELMAH exceptions that have happened on that site.

Untitled

This allows me to go back through the Production logs to get a better feel for recurring issues.  Since Production has multiple, load-balanced servers, having a separate error report that is tied to each server allows me to identify problems that are occurring on only that web server.

With the new MVC site, however, my first attempts to browse to ~/errors/report.axd resulted in a 404.  After a little thought, I theorized that ASP.NET was seeing the request for this page and first checked to see if it existed as a file on the file system.  When it didn’t it moved on to the routes I defined in Application_Start in Global.asax.  It didn’t find a route for that file, but it did see my “catch all” route at the bottom:

Public Class MvcApplication
Inherits System.Web.HttpApplication

Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
routes.IgnoreRoute(“{resource}.axd/{*pathInfo}”)

‘ MapRoute takes the following parameters, in order:
‘ (1) Route name
‘ (2) URL with parameters
‘ (3) Parameter defaults
routes.MapRoute( _
“Default”, _
“{controller}.aspx/{action}/{id}”, _
New With {.controller = “Home”, .action = “Index”, .id = UrlParameter.Optional} _
)

routes.MapRoute( _
“Catch All”, _
“{*path}”, _
New With {.controller = “Error”, .action = “NotFound”} _
)

End Sub

Sub Application_Start()
AreaRegistration.RegisterAllAreas()
RegisterRoutes(RouteTable.Routes)
End Sub
End Class

The catch all says “anything that doesn’t match one of the above rules should render the 404 page”.  And that was exactly what I was getting.  The search for report.axd stopped there because it finally matched a routing rule, and therefore the request never made it to ELMAH.

It was then that I noticed the routes.IgnoreRoute rule at the very top.  That looked interesting.  What if I added one like that for /errors/report.axd?  I added this as the second rule in the RegisterRoutes method:

routes.IgnoreRoute(“errors/report.axd”)

I tried the page again, and this time it worked!

Well, sort of.  The page rendered, but not any of the usual styling.  I fired up Fiddler and hit the page again.  I saw that requests for both errors/report.axd and errors/report.axd/stylesheet.  Aha!  I bet my rule doesn’t cover that.

I looked at the original .IgnoreRoute rule, and saw that the were using (*pathInfo) in the definition.  That looked a lot like a wildcard, so I tried adding a variant of my first rule to the list.

routes.IgnoreRoute(“errors/report.axd”)

routes.IgnoreRoute(“errors/report.axd/(*pathInfo)”)

Hey, look at that!  The page renders beautifully now!  Ok, now it was time to tempt fate.  I wondered if the first rule was actually a special case of the second, and if the second would actually cover everything.  I commented the first rule out and tried the page again.  Success!  The page still looks and functions beautifully.  I removed the first rule and committed everything.

My initial doubts were laid to rest.  Onward!

Advertisements

February 5, 2011 - Posted by | ASP.NET MVC, Visual Studio/.NET

Sorry, the comment form is closed at this time.

%d bloggers like this: