Custom 404 Pages for ASP.NET MVC 3

We are just about to release a new version of our corporate website, and one of the last things I needed to do was handle 404’s gracefully. We did a major overhaul of the information architecture, so there is a very good chance that some people will get 404’s from existing bookmarks.

Turns out, it all starts in Global.asax.

Unlike a Web Forms app, where a url translates to a file on disk, MVC uses a set of routes to invoke methods on controller. So, when a request comes in with a bogus Url, say http://dtsams.com/bogus, the app actually throws an exception because it’s trying to instantiate BogusController, which of course is not part of the application. To catch this exception, we implement Application_Error in Global.asax

protected void Application_Error()
{
    var exception = Server.GetLastError();
    var httpException = exception as HttpException;
    Response.Clear();
    Server.ClearError();
    var routeData = new RouteData();
    routeData.Values["controller"] = "Errors";
    routeData.Values["action"] = "General";
    routeData.Values["exception"] = exception;
    Response.StatusCode = 500;
    if (httpException != null)
    {
        Response.StatusCode = httpException.GetHttpCode();
        switch (Response.StatusCode)
        {
            case 403:
                routeData.Values["action"] = "Http403";
                break;
            case 404:
                routeData.Values["action"] = "Http404";
                break;
        }
    }           
    // Avoid IIS7 getting in the middle
    Response.TrySkipIisCustomErrors = true; 
    IController errorsController = new ErrorsController();
    HttpContextWrapper wrapper = new HttpContextWrapper(Context);
    var rc = new RequestContext(wrapper, routeData);
    errorsController.Execute(rc);
}

So, looking at this, we basically get the exception, clear the response, setup some new RouteData, look at the exception, and then send things off the the Errors controller.

Of note, the Response.TrySkipIisCustomErrors line is critical when you deploy to IIS 7.5, and it took me a fair bit of Googling and hacking to finally get this working. Without this line, I’d get routed to the Errors controller correctly on my local IIS, but I’d just get the IIS 404 screen when I pushed to the production web server.

So, clearly this means we need an Errors controller – this is a simple example – 3 actions, just dropping to views.

public class ErrorsController : Controller
{
    public ActionResult General(Exception exception)
    {
        return View("Exception",exception);
    }

    public ActionResult Http404()
    {
        return View("404");
    }

    public ActionResult Http403()
    {
        return View("403");
    }
}

You’ll likely want to add more logic to log real exceptions, and record the 404’s so that if there is a common 404, you can add some additional logic to make recommendations, or automatically forward the user to the correct location.

While this is relatively simple stuff, for some reason it’s not baked into the project templates – yet another thing to add into our templates I guess!

13 thoughts on “Custom 404 Pages for ASP.NET MVC 3

  1. Robert Beal (@robertbeal)

    Awesome solution, just what I wanted. To take things a step further, you can reduce the code in Global.asax and make it testable, ie correct RouteData, Response is cleared etc…

    Wrap the context, _container.Resolve().Handle(new HttpContextWrapper(Context)), sort of thing.

    In my case, the ExceptionHandler.Handle then loops through a dynamically registered list of IExceptionHandlers. If an IExceptionHandler can handle the exception it does. This may or may not mean executing the ErrorController (Redirects). If none of them can handle it, you fall back to a generic solution. Can also inject an ILog for logging.

    Reply
    1. Dave Bouwman Post author

      In that case, I think you’d want to redirect them to the home page or something like that. You could show this 404 page, and in there use a meta-refresh header to forward them to the homepage (or wherever) after a few seconds.

      Cheers,

      Dave

      Reply
  2. Laurits West

    OMG I have sought for this for two hours now.
    Been on so many forums saying one thing, another thing, and all without any of it working.
    This is simple to understand, easy to implement, and works like a charm!
    BRAVO Dave!

    Reply
  3. westdk

    Finally a working post!
    I have been searching for this for 2 hours now, and been to (I think) every possible forum on the living web all saying different things – all without working.
    This is easy to understand, fast to implement and it works like a charm as well.
    BRAVO Dave!
    I used it on csharpudvikler.dk to make a 404 Page that can help to find missing children (Notfound.org).

    Reply
  4. Pingback: Custom 404 page in ASP.NET MVC | /* @staurimas */

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>