ASP.NET MVC 404 Error Handling [duplicate]

I’ve investigated A LOT on how to properly manage 404s in MVC (specifically MVC3), and this, IMHO is the best solution I’ve come up with:

In global.asax:

public class MvcApplication : HttpApplication
{
    protected void Application_EndRequest()
    {
        if (Context.Response.StatusCode == 404)
        {
            Response.Clear();

            var rd = new RouteData();
            rd.DataTokens["area"] = "AreaName"; // In case controller is in another area
            rd.Values["controller"] = "Errors";
            rd.Values["action"] = "NotFound";

            IController c = new ErrorsController();
            c.Execute(new RequestContext(new HttpContextWrapper(Context), rd));
        }
    }
}

ErrorsController:

public sealed class ErrorsController : Controller
{
    public ActionResult NotFound()
    {
        ActionResult result;

        object model = Request.Url.PathAndQuery;

        if (!Request.IsAjaxRequest())
            result = View(model);
        else
            result = PartialView("_NotFound", model);

        return result;
    }
}

Edit:

If you’re using IoC (e.g. AutoFac), you should create your controller using:

var rc = new RequestContext(new HttpContextWrapper(Context), rd);
var c = ControllerBuilder.Current.GetControllerFactory().CreateController(rc, "Errors");
c.Execute(rc);

Instead of

IController c = new ErrorsController();
c.Execute(new RequestContext(new HttpContextWrapper(Context), rd));

(Optional)

Explanation:

There are 6 scenarios that I can think of where an ASP.NET MVC3 apps can generate 404s.

Generated by ASP.NET:

  • Scenario 1: URL does not match a route in the route table.

Generated by ASP.NET MVC:

  • Scenario 2: URL matches a route, but specifies a controller that doesn’t exist.

  • Scenario 3: URL matches a route, but specifies an action that doesn’t exist.

Manually generated:

  • Scenario 4: An action returns an HttpNotFoundResult by using the method HttpNotFound().

  • Scenario 5: An action throws an HttpException with the status code 404.

  • Scenario 6: An actions manually modifies the Response.StatusCode property to 404.

Objectives

  • (A) Show a custom 404 error page to the user.

  • (B) Maintain the 404 status code on the client response (specially important for SEO).

  • (C) Send the response directly, without involving a 302 redirection.

Solution Attempt: Custom Errors

<system.web>
    <customErrors mode="On">
        <error statusCode="404" redirect="~/Errors/NotFound"/>
    </customErrors>
</system.web>

Problems with this solution:

  • Does not comply with objective (A) in scenarios (1), (4), (6).
  • Does not comply with objective (B) automatically. It must be programmed manually.
  • Does not comply with objective (C).

Solution Attempt: HTTP Errors

<system.webServer>
    <httpErrors errorMode="Custom">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

Problems with this solution:

  • Only works on IIS 7+.
  • Does not comply with objective (A) in scenarios (2), (3), (5).
  • Does not comply with objective (B) automatically. It must be programmed manually.

Solution Attempt: HTTP Errors with Replace

<system.webServer>
    <httpErrors errorMode="Custom" existingResponse="Replace">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

Problems with this solution:

  • Only works on IIS 7+.
  • Does not comply with objective (B) automatically. It must be programmed manually.
  • It obscures application level http exceptions. E.g. can’t use customErrors section, System.Web.Mvc.HandleErrorAttribute, etc. It can’t only show generic error pages.

Solution Attempt customErrors and HTTP Errors

<system.web>
    <customErrors mode="On">
        <error statusCode="404" redirect="~/Errors/NotFound"/>
    </customError>
</system.web>

and

<system.webServer>
    <httpErrors errorMode="Custom">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

Problems with this solution:

  • Only works on IIS 7+.
  • Does not comply with objective (B) automatically. It must be programmed manually.
  • Does not comply with objective (C) in scenarios (2), (3), (5).

People that have troubled with this before even tried to create their own libraries (see http://aboutcode.net/2011/02/26/handling-not-found-with-asp-net-mvc3.html). But the previous solution seems to cover all the scenarios without the complexity of using an external library.

Leave a Comment