2009-05-01 20 views
63

¿Cómo puedo manejar correctamente las excepciones lanzadas desde los controladores en ASP.NET MVC? El atributo HandleError parece que solo procesa las excepciones lanzadas por la infraestructura de MVC y no las excepciones lanzadas por mi propio código.Error al manejar en ASP.NET MVC

El uso de este web.config

<customErrors mode="On"> 
    <error statusCode="401" redirect="/Errors/Http401" /> 
</customErrors> 

con el siguiente código

namespace MvcApplication1.Controllers 
{ 
    [HandleError] 
    public class HomeController : Controller 
    { 
     public ActionResult Index() 
     { 
      // Force a 401 exception for testing 
      throw new HttpException(401, "Unauthorized"); 
     } 
    } 
} 

no da lugar a lo que yo esperaba. En cambio, obtengo la página de error genérica ASP.NET que me indica que modifique mi web.config para ver la información de error real. Sin embargo, si en lugar de lanzar una excepción vuelvo una vista válido, tengo la página /Shared/Views/Error.aspx:

return View("DoesNotExist"); 

lanzar excepciones dentro de un controlador que he hecho anterior parece eludir todos la funcionalidad HandleError, entonces, ¿cuál es la forma correcta de crear páginas de error y cómo juego bien con la infraestructura MVC?

Respuesta

20

Gracias a kazimanzurrashaid, esto es lo que terminé haciendo en Global.asax.cs:

protected void Application_Error() 
{ 
    Exception unhandledException = Server.GetLastError(); 
    HttpException httpException = unhandledException as HttpException; 
    if (httpException == null) 
    { 
     Exception innerException = unhandledException.InnerException; 
     httpException = innerException as HttpException; 
    } 

    if (httpException != null) 
    { 
     int httpCode = httpException.GetHttpCode(); 
     switch (httpCode) 
     { 
      case (int) HttpStatusCode.Unauthorized: 
       Response.Redirect("/Http/Error401"); 
       break; 
     } 
    } 
} 

Podré agregar más páginas al HttpContoller en función de cualquier código de error HTTP adicional que necesite.

+1

Le sugiero que compruebe el origen de http://kigg.codeplex.com, donde encontrará el código fuente completo de este httpModule, solo quiero saturar global.asax y por eso el HttpModule. – kazimanzurrashid

13

El atributo HandleError parece que solo procesa las excepciones lanzadas por la infraestructura de MVC y no las excepciones lanzadas por mi propio código.

Eso es simplemente incorrecto. De hecho, HandleError solo "procesará" excepciones lanzadas en su propio código o en código llamado por su propio código. En otras palabras, solo excepciones donde su acción está en la pila de llamadas.

La verdadera explicación del comportamiento que está viendo es la excepción específica que está lanzando. HandleError se comporta de manera diferente con una HttpException. A partir del código fuente:

 // If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method), 
     // ignore it. 
     if (new HttpException(null, exception).GetHttpCode() != 500) { 
      return; 
     } 
+0

Usted ha señalado que una de mis afirmaciones es incorrecta sin abordar el verdadero problema de no ser capaz de proporcionar control de errores para HTTP códigos de error –

+4

Puedo explicar lo que está sucediendo, pero necesitaría más información para decirle qué es lo correcto para su aplicación. HandleError funciona en una acción, y generalmente no arrojas HttpExceptions dentro de una acción. En lugar de adivinar lo que en realidad estás tratando de hacer y darte consejos potencialmente incorrectos, puedo explicar los hechos detrás del comportamiento que estás viendo. Por favor, no dude en actualizar la pregunta si desea explicar más acerca de lo que realmente quiere hacer. –

63

Controller.OnException(ExceptionContext context). Anularlo.

protected override void OnException(ExceptionContext filterContext) 
{ 
    // Bail if we can't do anything; app will crash. 
    if (filterContext == null) 
     return; 
     // since we're handling this, log to elmah 

    var ex = filterContext.Exception ?? new Exception("No further information exists."); 
    LogException(ex); 

    filterContext.ExceptionHandled = true; 
    var data = new ErrorPresentation 
     { 
      ErrorMessage = HttpUtility.HtmlEncode(ex.Message), 
      TheException = ex, 
      ShowMessage = !(filterContext.Exception == null), 
      ShowLink = false 
     }; 
    filterContext.Result = View("ErrorPage", data); 
} 
+0

Sí, esta es la manera de hacerlo. Usar uno de los filtros proporcionados, o crear su propio filtro de excepción es el camino a seguir. Puede cambiar la vista según el tipo de excepción o profundizar más inspeccionando más información en su filtro personalizado. – a7drew

+0

¿Estás seguro de que esto no era algo que estaba en las versiones de prueba de MVC pero que fue cambiado para 1.0? No veo esta anulación, en su lugar parece que debe anular OnActionExecuted e inspeccionar el valor de filterContext.Exception –

+0

http://msdn.microsoft.com/en-us/library/system.web.mvc.controller.onexception (VS.100) .aspx – Will

6

No creo que va a ser capaz de mostrar ErrorPage específico basado en la HttpCode con el atributo HandleError y yo preferiría usar un HttpModule para este propósito. Suponiendo que tengo la carpeta "ErrorPages" donde existe una página diferente para cada error específico y la asignación se especifica en el archivo web.config igual que la aplicación de formulario web normal. Y el siguiente es el código que se utiliza para mostrar la página de error:

public class ErrorHandler : BaseHttpModule{ 

public override void OnError(HttpContextBase context) 
{ 
    Exception e = context.Server.GetLastError().GetBaseException(); 
    HttpException httpException = e as HttpException; 
    int statusCode = (int) HttpStatusCode.InternalServerError; 

    // Skip Page Not Found and Service not unavailable from logging 
    if (httpException != null) 
    { 
     statusCode = httpException.GetHttpCode(); 

     if ((statusCode != (int) HttpStatusCode.NotFound) && (statusCode != (int) HttpStatusCode.ServiceUnavailable)) 
     { 
      Log.Exception(e); 
     } 
    } 

    string redirectUrl = null; 

    if (context.IsCustomErrorEnabled) 
    { 
     CustomErrorsSection section = IoC.Resolve<IConfigurationManager>().GetSection<CustomErrorsSection>("system.web/customErrors"); 

     if (section != null) 
     { 
      redirectUrl = section.DefaultRedirect; 

      if (httpException != null) 
      { 
       if (section.Errors.Count > 0) 
       { 
        CustomError item = section.Errors[statusCode.ToString(Constants.CurrentCulture)]; 

        if (item != null) 
        { 
         redirectUrl = item.Redirect; 
        } 
       } 
      } 
     } 
    } 

    context.Response.Clear(); 
    context.Response.StatusCode = statusCode; 
    context.Response.TrySkipIisCustomErrors = true; 

    context.ClearError(); 

    if (!string.IsNullOrEmpty(redirectUrl)) 
    { 
     context.Server.Transfer(redirectUrl); 
    } 
} 

}

+0

Esto es lo que estaba buscando.Tomé un enfoque un poco más simple y puse mi código en Application_Error para manejar el mismo conjunto de errores en los controladores. Voy a publicar la solución en una respuesta a esta pregunta. –

3

Otra posibilidad (no es cierto en su caso) que otros leen esto pueden estar experimentando es que su página de error está lanzando un error en sí, o no está implementando:

System.Web.Mvc.ViewPage<System.Web.Mvc.HandleErrorInfo> 

Si este es el caso, entonces obtendrá la página de error predeterminada (de lo contrario, obtendría un bucle infinito porque seguiría intentando enviarse a su página de error personalizada).Esto no fue inmediatamente obvio para mí.

Este modelo es el modelo enviado a la página de error. Si su página de error utiliza la misma página maestra que el resto de su sitio y requiere cualquier otra información del modelo, deberá crear su propio tipo de atributo [HandleError] o anular OnException o algo así.

+0

También podría declarar el atributo [HandleError] en sus controladores o métodos de controlador, pero no ayuda con los errores de HTTP. –

2

Elegí el enfoque Controller.OnException(), que para mí es la opción lógica: como he elegido ASP.NET MVC, prefiero mantenerme en el nivel del marco y evitar jugar con la mecánica subyacente, si es posible.

me encontré con el siguiente problema:

Si se produce la excepción dentro de la vista, la salida parcial de la vista que aparecerá en la pantalla, junto con el mensaje de error.

que fija esta en la limpieza de la respuesta, antes de filterContext.Result - como esto:

 filterContext.HttpContext.Response.Clear(); // gets rid of any garbage 
     filterContext.Result = View("ErrorPage", data); 

Hope esto ahorra a otra persona algún tiempo :-)

+0

Parece que está replicando el comportamiento ya previsto por el atributo HandleError. –

+2

Posiblemente. Aprender todo esto es un esfuerzo doloroso. Tantos ejemplos malos y documentación desactualizada para filtrar. Supongo que acabo de contribuir con otro mal ejemplo ;-) –

2
 protected override void OnException (ExceptionContext filterContext) 
    { 
     if (filterContext != null && filterContext.Exception != null) 
     { 
      filterContext.ExceptionHandled = true; 
      this.View("Error").ViewData["Exception"] = filterContext.Exception.Message; 
      this.View("Error").ExecuteResult(this.ControllerContext); 
     } 
    } 
+1

Es posible que desee almacenar el resultado de 'this.View (" Error ")' en una variable local en lugar de invocar el método 2 veces. – SandRock

1

Jeff Atwood User Friendly Exception Handling module funciona muy bien para MVC. Se pueden configurar por completo en su web.config, sin código fuente del proyecto MVC cambia en absoluto. Sin embargo, necesita una pequeña modificación para devolver el estado HTTP original en lugar de un estado 200. Consulte esto relacionado forum post.

Básicamente, en Handler.vb, se puede añadir algo como:

' In the header... 
Private _exHttpEx As HttpException = Nothing 

' At the top of Public Sub HandleException(ByVal ex As Exception)... 
HttpContext.Current.Response.StatusCode = 500 
If TypeOf ex Is HttpException Then 
    _exHttpEx = CType(ex, HttpException) 
    HttpContext.Current.Response.StatusCode = _exHttpEx.GetHttpCode() 
End If 
Cuestiones relacionadas