Manejo de errores ASP.NET MVC 404 [duplicado]

Resuelto Clearly asked hace 15 años • 6 respuestas

Posible duplicado:
¿Cómo puedo manejar correctamente 404 en ASP.NET MVC?

Hice los cambios descritos en el controlador de errores 404 Http en Asp.Net MVC (RC 5) y todavía recibo la página de error 404 estándar. ¿Necesito cambiar algo en IIS?

Clearly avatar Apr 05 '09 02:04 Clearly
Aceptado

He investigado MUCHO sobre cómo administrar adecuadamente los 404 en MVC (específicamente MVC3) , y esta, en mi humilde opinión, es la mejor solución que se me ocurrió:

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));
        }
    }
}

Controlador de errores:

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 (por ejemplo, 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 en los que puedo pensar en los que una aplicación ASP.NET MVC3 puede generar 404.

Generado por ASP.NET:

  • Escenario 1: la URL no coincide con una ruta en la tabla de rutas.

Generado por ASP.NET MVC:

  • Escenario 2: la 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.

Generado manualmente:

  • Escenario 4: una acción devuelve un HttpNotFoundResult utilizando el método HttpNotFound().

  • Escenario 5: una acción genera una HttpException con el código de estado 404.

  • Escenario 6: una acción modifica manualmente la propiedad Response.StatusCode a 404.

Objetivos

  • (A) Muestre una página de error 404 personalizada al usuario.

  • (B) Mantener el código de estado 404 en la respuesta del cliente (especialmente importante para SEO).

  • (C) Enviar la respuesta directamente, sin que implique una redirección 302.

Intento de solución: errores personalizados

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

Problemas con esta solución:

  • No cumple el objetivo (A) en los escenarios (1), (4), (6).
  • No cumple el objetivo (B) automáticamente. Debe programarse manualmente.
  • No cumple con el objetivo (C).

Intento de solución: 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 el objetivo (A) en los escenarios (2), (3), (5).
  • No cumple el objetivo (B) automáticamente. Debe programarse manualmente.

Intento de solución: errores HTTP con reemplazo

<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 el objetivo (B) automáticamente. Debe programarse manualmente.
  • Oculta las excepciones http a nivel de aplicación. Por ejemplo, no puede usar la sección customErrors, System.Web.Mvc.HandleErrorAttribute, etc. No solo puede mostrar páginas de error genéricas.

Intento de solución de errores personalizados 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 el objetivo (B) automáticamente. Debe programarse manualmente.
  • No cumple 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 utilizar una biblioteca externa.

Marco avatar Jan 26 '2012 23:01 Marco

Otra solución más.

Agregue ErrorControllers o una página estática con información de error 404.

Modifique su web.config (en el caso de controlador).

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

O en caso de página estática

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

Esto manejará tanto las rutas perdidas como las acciones perdidas.

Mike Chaliy avatar Apr 04 '2009 20:04 Mike Chaliy

La respuesta de Marco es la MEJOR solución. Necesitaba controlar mi manejo de errores, y quiero decir realmente CONTROLARLO. Por supuesto, amplié un poco la solución y creé un sistema de gestión de errores completo que gestiona todo. También he leído sobre esta solución en otros blogs y parece muy aceptable para la mayoría de los desarrolladores avanzados.

Aquí está el código final que estoy usando:

protected void Application_EndRequest()
    {
        if (Context.Response.StatusCode == 404)
        {
            var exception = Server.GetLastError();
            var httpException = exception as HttpException;
            Response.Clear();
            Server.ClearError();
            var routeData = new RouteData();
            routeData.Values["controller"] = "ErrorManager";
            routeData.Values["action"] = "Fire404Error";
            routeData.Values["exception"] = exception;
            Response.StatusCode = 500;

            if (httpException != null)
            {
                Response.StatusCode = httpException.GetHttpCode();
                switch (Response.StatusCode)
                {
                    case 404:
                        routeData.Values["action"] = "Fire404Error";
                        break;
                }
            }
            // Avoid IIS7 getting in the middle
            Response.TrySkipIisCustomErrors = true;
            IController errormanagerController = new ErrorManagerController();
            HttpContextWrapper wrapper = new HttpContextWrapper(Context);
            var rc = new RequestContext(wrapper, routeData);
            errormanagerController.Execute(rc);
        }
    }

y dentro de mi ErrorManagerController:

        public void Fire404Error(HttpException exception)
    {
        //you can place any other error handling code here
        throw new PageNotFoundException("page or resource");
    }

Ahora, en mi Acción, estoy lanzando una excepción personalizada que he creado. Y mi controlador hereda de una clase personalizada basada en controlador que he creado. El controlador base personalizado se creó para anular el manejo de errores. Aquí está mi clase de controlador base personalizada:

public class MyBasePageController : Controller
{
    protected override void OnException(ExceptionContext filterContext)
    {
        filterContext.GetType();
        filterContext.ExceptionHandled = true;
        this.View("ErrorManager", filterContext).ExecuteResult(this.ControllerContext);
        base.OnException(filterContext);
    }
}

El "ErrorManager" en el código anterior es solo una vista que utiliza un modelo basado en ExceptionContext

Mi solución funciona perfectamente y puedo manejar CUALQUIER error en mi sitio web y mostrar diferentes mensajes según CUALQUIER tipo de excepción.

Yousi avatar Apr 11 '2012 13:04 Yousi

Parece que esta es la mejor manera de captarlo todo.

¿Cómo puedo manejar correctamente 404 en ASP.NET MVC?

Clearly avatar Apr 04 '2009 20:04 Clearly