He investigado MUCHO sobre cómo manejar adecuadamente 404s en MVC (específicamente MVC3), y esto, en mi humilde opinión es la mejor solución que he llegado con:
En 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;
}
}
Editar :
Si está utilizando IoC (p. Ej.Autofac), debe crear su controlador usando:
var rc = new RequestContext(new HttpContextWrapper(Context), rd);
var c = ControllerBuilder.Current.GetControllerFactory().CreateController(rc, "Errors");
c.Execute(rc);
En lugar de
IController c = new ErrorsController();
c.Execute(new RequestContext(new HttpContextWrapper(Context), rd));
(Opcional)
Explicación:
Hay 6 escenarios que pueda pensar en dónde una aplicación ASP.NET MVC3 puede generar 404s.
generado por ASP.NET:
- Escenario 1: URL no coincide con una ruta en la tabla de rutas.
generada por ASP.NET MVC:
Escenario 2: URL coincide con una ruta, pero especifica un controlador que no existe.
Escenario 3: La URL coincide con una ruta, pero especifica una acción que no existe.
generada manualmente:
Escenario 4: Una acción devuelve un HttpNotFoundResult utilizando el método HttpNotFound().
Escenario 5: Una acción produce una HttpException con el código de estado 404.
Escenario 6: Un acciones modifica manualmente la propiedad Response.StatusCode a 404.
Objetivos
(A) Mostrar una página de error 404 personalizada para el usuario.
(B) Mantener el código de estado 404 en la respuesta del cliente (especialmente importante para SEO).
(C) Envíe la respuesta directamente, sin incluir una redirección 302.
Solución Intento: Errores personalizados
<system.web>
<customErrors mode="On">
<error statusCode="404" redirect="~/Errors/NotFound"/>
</customError>
</system.web>
Los problemas con esta solución:
- no cumple con el objetivo (A) en los escenarios (1), (4), (6)
- No cumple con el objetivo (B) automáticamente. Debe ser programado manualmente.
- No cumple con el objetivo (C).
Solución Intento: Errores HTTP
<system.webServer>
<httpErrors errorMode="Custom">
<remove statusCode="404"/>
<error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
</httpErrors>
</system.webServer>
problemas con esta solución:
- sólo funciona en IIS 7+.
- No cumple con el objetivo (A) en los escenarios (2), (3), (5).
- No cumple con el objetivo (B) automáticamente. Debe ser programado manualmente.
Solución Intento: HTTP Errores con Reemplazar
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404"/>
<error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
</httpErrors>
</system.webServer>
problemas con esta solución:
- sólo funciona en IIS 7+.
- No cumple con el objetivo (B) automáticamente. Debe ser programado manualmente.
- Oculta las excepciones http de nivel de aplicación. P.ej. no puede usar la sección customErrors, System.Web.Mvc.HandleErrorAttribute, etc. No solo puede mostrar páginas de error genéricas.
customErrors de intento de soluciones y Errores HTTP
<system.web>
<customErrors mode="On">
<error statusCode="404" redirect="~/Errors/NotFound"/>
</customError>
</system.web>
y
<system.webServer>
<httpErrors errorMode="Custom">
<remove statusCode="404"/>
<error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
</httpErrors>
</system.webServer>
problemas con esta solución:
- sólo funciona en IIS 7+.
- No cumple con el objetivo (B) automáticamente. Debe ser programado manualmente.
- No cumple con el objetivo (C) en los escenarios (2), (3), (5).
Las personas que han tenido problemas con esto antes incluso intentaron crear sus propias bibliotecas (consulte http://aboutcode.net/2011/02/26/handling-not-found-with-asp-net-mvc3.html). Pero la solución anterior parece cubrir todos los escenarios sin la complejidad de usar una biblioteca externa.
Aquí hay una buena lectura sobre este tema @ [Cómo manejar 404 errores no encontrados de manera efectiva con ASP.NET MVC 4] (http://yassershaikh.com/how-to-handle-404-not-found-errors -effectively-with-asp-net-mvc-4 /) – Yasser