2009-06-23 14 views
22

Estoy buscando una solución simple para hacer el registro de excepciones combinado con el manejo de errores en mi aplicación ASP.Net MVC 1.0.ASP.Net MVC Exception Logging combinado con manejo de errores

He leído muchos artículos, incluidas las preguntas publicadas aquí en StackOverflow, que proporcionan diversas soluciones para diferentes situaciones. Todavía no puedo encontrar una solución que se adapte a mis necesidades.

Éstos son mis requisitos:

  1. Para poder utilizar el atributo [HandleError] (o su equivalente) en mi controlador, para manejar todas las excepciones que puedan ser lanzados desde cualquiera de las acciones o Vistas . Esto debería manejar todas las excepciones que no se manejaron específicamente en ninguna de las Acciones (como se describe en el punto 2). Me gustaría poder especificar a qué vista se debe redirigir a un usuario en casos de error, para todas las acciones en el controlador.

  2. Quiero poder especificar el atributo [HandleError] (o algo equivalente) en la parte superior de Acciones específicas para atrapar excepciones específicas y redirigir a los usuarios a una Vista adecuada a la excepción. Todas las demás excepciones deben ser manejadas por el atributo [HandleError] en el controlador.

  3. En ambos casos, quiero que las excepciones se registren usando log4net (o cualquier otra biblioteca de registro).

¿Cómo hago para lograr lo anterior? He leído acerca de hacer que todos mis Controladores hereden de un controlador base que anula el método OnException, y en el que hago mi registro. Sin embargo, esto complicará la tarea de redireccionar a los usuarios a las Vistas apropiadas o hacerlo desordenado.

He leído acerca de cómo escribir mi propia Acción de filtro que implementa IExceptionFilter para manejar esto, pero esto entrará en conflicto con el atributo [HandleError].

Hasta ahora, pienso que la mejor solución es escribir mi propio atributo heredado de HandleErrorAttribute. De esa manera, obtengo toda la funcionalidad de [HandleError] y puedo agregar mi propio log4net logging. La solución es la siguiente:

public class HandleErrorsAttribute: HandleErrorAttribute { 

     private log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 

     public override void OnException(ExceptionContext filterContext) 
     { 
      if (filterContext.Exception != null) 
      { 
      log.Error("Error in Controller", filterContext.Exception); 
      } 

      base.OnException(filterContext); 
     } 
    } 

¿Funcionará el código anterior para mis requisitos? Si no, ¿qué solución cumple mis requisitos?

Respuesta

24

Todavía estoy un poco confundido con todas las diferentes soluciones por ahí, y cómo los atributos pueden interferir entre sí, pero fue con esta solución:

public class LogErrorsAttribute: FilterAttribute, IExceptionFilter 
{ 
    #region IExceptionFilter Members 

    void IExceptionFilter.OnException(ExceptionContext filterContext) 
    { 
     if (filterContext != null && filterContext.Exception != null) 
     { 
      string controller = filterContext.RouteData.Values["controller"].ToString(); 
      string action = filterContext.RouteData.Values["action"].ToString(); 
      string loggerName = string.Format("{0}Controller.{1}", controller, action); 

      log4net.LogManager.GetLogger(loggerName).Error(string.Empty, filterContext.Exception); 
     } 

    } 

    #endregion 
} 

todavía uso el atributo [HandleError] como se explicó en la pregunta original, y yo solo decorar cada controlador con un atributo [LogErrors].

Esto funciona para mí, ya que mantiene el registro de errores en un solo lugar y no hace que las excepciones duplicadas se registren varias veces (lo que sucederá si extiendo [HandleError] y uso el atributo en varios lugares).

Yo no creo que vaya a ser posible combinar tanto el registro de excepciones y tratamiento de errores en uno atrribuya o clase, sin que se convierta muy tedioso y complejo, o que afecten a la utilización de [HandleError]

Pero esto funciona para mí ya que decorar cada controlador solo una vez, con el atributo [LogErrors], y decorar Controladores y Acciones con [HandleError] exactamente como quiero, sin que interfieran entre sí.

Actualización:

Aquí es un ejemplo de cómo lo uso:

[LogErrors(Order = 0)] 
[HandleError(Order = 99)] 
public class ContactController : Controller 
{ 
    public ActionResult Index() 
    { 
     return View(Views.Index); 
    } 

    public ActionResult Directions() 
    { 
     return View(Views.Directions); 
    } 


    public ActionResult ContactForm() 
    { 
     FormContactMessage formContactMessage = new FormContactMessage(); 

     return View(Views.ContactForm,formContactMessage); 
    } 

    [HandleError(ExceptionType = typeof(SmtpException), View = "MessageFailed", Order = 1)] 
    [AcceptVerbs(HttpVerbs.Post)] 
    public ActionResult ContactForm(FormContactMessage formContactMessage) 
    { 
     if (ModelState.IsValid) 
     { 
      if (formContactMessage.IsValid) 
      { 
       SmtpClient client = new SmtpClient(); 

       MailAddress recipientAddress = new MailAddress(Properties.Settings.Default.ContactFormRecipientEmailAddress); 
       MailAddress senderAddress = new MailAddress(Properties.Settings.Default.ContactFormSenderEmailAddress); 
       MailMessage mailMessage = formContactMessage.ToMailMessage(recipientAddress, senderAddress); 

       client.Send(mailMessage); 

       return View("MessageSent"); 
      } 
      else 
      { 
       ModelState.AddRuleViolations(formContactMessage.GetRuleViolations()); 
      } 
     } 
     return View(Views.ContactForm, formContactMessage); 
    } 

    private static class Views 
    { 
     public static string Index { get { return "Index"; } } 
     public static string Directions { get { return "Directions"; } } 
     public static string ContactForm { get { return "ContactForm"; } } 

    } 
} 

En el código anterior, SmtpExceptions en la sobrecarga de ContactForm acción se manejan de una manera muy específica - el usuario se presenta con una página de visualización específica para mensajes enviados fallidos, en este caso se llama "MessageFailed". Todas las demás excepciones son manejadas por el comportamiento predeterminado de [HandleError]. También tenga en cuenta que el registro de errores se produce primero, seguido de la gestión de errores. Esto se indica por el siguiente:

[LogErrors(Order = 0)] 
[HandleError(Order = 99)] 

Actualización:

Hay una solución alternativa a este, con una muy buena explanantion. Recomiendo leerlo para obtener una mejor comprensión de los problemas involucrados.

ASP.NET MVC HandleError Attribute, Custom Error Pages and Logging Exceptions (Gracias a Scott Shepherd a continuación, que proporcionó el enlace en una respuesta más abajo).

+0

Bien, muchas gracias, voy a usar esto. – Kezzer

+3

Sospecho que este código tiene un error en el que se supone que null no se devuelve para RouteData.Values ​​["action"] - al llamar .ToString() podría causar que el manejador de errores arroje una NullReferenceException. Nada es más frustrante que un controlador de error arrojar un error. –