2009-04-04 19 views
119

Duplicar posible:
How can I properly handle 404 in ASP.NET MVC?ASP.NET MVC 404 Manejo de errores

he hecho los cambios descritos en 404 Http error handler in Asp.Net MVC (RC 5) y todavía estoy recibiendo la página de error 404 estándar. ¿Debo cambiar algo en IIS?

+1

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

Respuesta

131

Sin embargo, otra solución.

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

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

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

O en el caso de la página estática

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

Esto manejar tanto las rutas perdidas y perdidas acciones.

+2

¡Agradable! :) ErrorsController podría heredar de la misma base que todos los demás controladores y así tener acceso a una determinada funcionalidad. Además, la vista de Error404 podría envolverse en el maestro, proporcionando al usuario una apariencia general del resto del sitio sin ningún trabajo adicional. – Dimskiy

+7

Utilice '' para ver la página de error real durante el desarrollo. –

+1

Esto es correcto. No llame a Response.Clear(); según lo sugerido por Mike Chaliy ver http://blogs.msdn.com/b/rickandy/archive/2012/03/01/response-redirect-and-asp-net-mvc-do-not-mix.aspx – RickAndMSFT

0

En IIS, puede especificar un redireccionamiento a la página "determinada" según el código de error. En su ejemplo, puede configurar 404 -> Su página de error 404 personalizada.

1

Lo que puedo recomendar es buscar en FilterAttribute. Por ejemplo, MVC ya tiene HandleErrorAttribute. Puedes personalizarlo para que maneje solo 404. Responde si estás interesado veré un ejemplo.

Por cierto

solución (con la última ruta) que ha aceptado en la pregunta anterior no funciona en gran parte de las situaciones. La segunda solución con HandleUnknownAction funcionará, pero es necesario realizar este cambio en cada controlador o tener un único controlador de base.

Mi elección es una solución con HandleUnknownAction.

+0

Parece que el problema es que la ruta estándar predeterminada de "{controller}/{action}/{id}" detecta everythgin para que no llegue al última ruta Pensé que si no se podía encontrar un controlador, se evaluaría la siguiente ruta. – Clearly

+0

HandleUnknownAction solo funciona con acciones que no son ruidosas. ¿Qué pasa si una ruta está amtched pero no se puede encontrar un controlador resultante? ¿Cuál es la mejor manera de manejar eso? – Clearly

+0

Sí, esto es correcto, solo cuando no se encuentra acción. Puede intentar combinar ambas soluciones. HandleUnknownAction para acciones perdidas y ruta para controladores perdidos. Otra posible solución es CustomHandler personalizado. –

340

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.

+0

Junto con HandleErrorAttribute que la plantilla MVC configura automáticamente cuando se crea un nuevo proyecto, esta es definitivamente la mejor y más simple solución. – s1mm0t

+6

Me gusta su análisis, pero no su solución :) los problemas con la espera hasta el final de la solicitud son que parte del contexto de la solicitud ya se ha descartado, como el estado de la sesión. –

+1

Response.Clear y MVC no se mezclan, no use ese enfoque http://blogs.msdn.com/b/rickandy/archive/2012/03/01/response-redirect-and-asp-net-mvc-do -not-mix.aspx – RickAndMSFT

5

La respuesta de Marco es la MEJOR solución. Necesitaba controlar mi manejo de errores, y me refiero a realmente CONTROLARLO. Por supuesto, amplié un poco la solución y creé un sistema completo de administración de errores que maneja 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í es 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 tirando de una excepción personalizada que he creado. Y mi controlador está heredando de una clase personalizada basada en el controlador que he creado. El controlador base personalizado se creó para anular el manejo de errores. Aquí es mi costumbre clase base:

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 sólo una vista que utiliza un modelo basado en ExceptionContext

Mi solución funciona perfectamente y yo soy capaz de manejar cualquier error en mi sitio web y mostrar diferentes mensajes basados ​​en CUALQUIER tipo de excepción.

+1

no estoy de acuerdo con usted en que es la MEJOR solución. Una tarea común como esta no debería ser tan complicada de configurar. La respuesta de Marcos es genial, pero realmente no quieres ese código para cosas simples. – PussInBoots

Cuestiones relacionadas