How to get normal 404 (Page not found) error pages using ASP.Net MVC
Currently there is no built-in mechanism to achieve regular 404 error pages from an MVC app. If a user tries to access /Products/Foo, or even /Foo, and Foo is not an action, you'll get an error message, similar to this:
The controller for path '/Foo' could not be found or it does not implement the IController interface
What we really want is to have a 404 error message generated. You can then optionally (ideally) redirect the user to a friendly error page appropriately styled for your site.
Fortunately the last bit we get for free with ASP.Net. Modifying your web.config to something similar to below would normally achieve this.
<customErrors mode="On" defaultRedirect="/Error">
<error statusCode="404" redirect="My404.html" />
</customErrors>
But out of the box MVC (preview 2) never generates the 404 error, so this functionality never kicks in. No doubt the MVC team will rectify this in a future release, but for now let's see if we can improve things.
Two modifications are needed :
a) To handle the unknown action (/Products/Foo)
b) To handle the unknown controller (/Foo)
a) This is the easier bit. Ensure your Controllers subclass your own common controller base class :
public class HomeController : ControllerBase { //.. }
This base class is a convenient place to put all sorts of common functionality - if you didn't need it to accomplish this particular task, you'll probably need to do it for something else, so it's good practice to have one.
public class ControllerBase : System.Web.Mvc.Controller { protected override void HandleUnknownAction(string actionName) { throw new HttpException(404, ""); } //... }
As you can see, we override the default behaviour of Mvc.Controller, when an unknown action is specified. By throwing the 404 ourselves, we induce the default ASP.Net behaviour specified in the web.config, which is what we want - redirection to our custom error page.
b) Handling the unknown controller is done by changing the Current ControllerBuilder's default Controller Factory. We create our own factory that does nothing more than throw that same HttpException, in the case that the controller could not be found.
public class IntelligentControllerFactory : System.Web.Mvc.DefaultControllerFactory { protected override IController CreateController(RequestContext requestContext, string controllerName) { IController controller = null; try { controller = base.CreateController(requestContext, controllerName); } catch (ArgumentNullException ex) //Note : this exception type may change with future releases { throw new HttpException(404, ""); } return controller; } }
Now we need to set up this factory to be used instead of the default, which is done in your global.asax.cs :
protected void Application_Start(object sender, EventArgs e) { ControllerBuilder.Current.SetControllerFactory( new IntelligentControllerFactory()); RegisterRoutes(RouteTable.Routes); //.... }
You should now be in 404 bliss.
If you have found a better way to achieve this, drop me a line.
This entry was posted on Saturday, May 24th, 2008 at 6:55 pm and is filed under Uncategorized. You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed.
August 17th, 2008 at 11:05 am
Richard Dingwall » Strategies for resource-based 404 errors in ASP.NET MVC says:[...] For invalid controllers and controller actions, you can implement your own IControllerFactory with inbuilt error handling. Nicholas Smit describes one here. [...]
October 20th, 2008 at 4:52 am
Nice work - thanks