ASP.Net MVC异常日志记录与错误处理相结合

ASP.Net MVC Exception Logging combined with Error Handling

我正在寻找一个简单的解决方案,在我的ASP.Net MVC 1.0应用程序中结合错误处理进行异常记录。

我已经阅读了很多文章,包括StackOverflow上发布的问题,这些文章都针对不同情况提供了不同的解决方案。我仍然无法提出适合我需求的解决方案。

这是我的要求:

  • 为了能够在我的Controller上使用[HandleError]属性(或类似的东西),处理可能从任何Actions或Views中抛出的所有异常。这应该处理所有未在任何操作上专门处理的异常(如第2点所述)。对于Controller中的所有操作,我希望能够指定在错误情况下必须将用户重定向到哪个View。

  • 我希望能够在特定Actions的顶部指定[HandleError]属性(或等效的东西)以捕获特定异常并将用户重定向到适合该异常的View。所有其他异常仍必须由Controller上的[HandleError]属性处理。

  • 在上述两种情况下,我都希望使用log4net(或任何其他日志记录库)记录异常。

  • 我如何实现上述目标?我已经阅读过让我的所有控制器继承自一个覆盖OnException方法的基本控制器,并且我在其中进行日志记录。但是,这会将用户重定向到适当的视图,或者使其变得混乱。

    我已经阅读过编写自己的Filter Action来实现IExceptionFilter来处理这个问题,但这会与[HandleError]属性冲突。

    到目前为止,我的想法是,最好的解决方案是编写自己继承自HandleErrorAttribute的属性。这样我就可以获得[HandleError]的所有功能,并且可以添加我自己的log4net日志记录。解决方案如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
        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);
          }
       }

    以上代码是否适用于我的要求?如果没有,哪种解决方案能满足我的要求?


    我仍然对所有不同的解决方案感到困惑,以及属性如何相互干扰,但我选择了这个解决方案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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
    }

    我仍然使用原始问题中解释的[HandleError]属性,我只是用[LogErrors]属性装饰每个控制器。

    这对我有用,因为它将错误记录保存在一个地方,并且不会导致多次记录重复的异常(如果我扩展[HandleError]并在多个地方使用该属性,将会发生这种情况)。

    我不认为可以将异常记录和错误处理结合到一个属性或类中,而不会变得非常繁琐和复杂,或影响[HandleError]的使用

    但这对我有用,因为我只使用[LogErrors]属性装饰每个控制器一次,并使用[HandleError]装饰控制器和动作完全按照我想要的方式,而不会相互干扰。

    更新:

    以下是我如何使用它的示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    [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"; } }

        }
    }

    在上面的代码中,ContactForm动作重载中的SmtpExceptions以非常特定的方式处理 - 向用户呈现特定于失败发送消息的ViewPage,在这种情况下,它被称为"MessageFailed"。所有其他异常都由[HandleError]的默认行为处理。另请注意,首先记录错误,然后处理错误。这由以下表示:

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

    更新:

    有一个替代解决方案,具有非常好的解释。我建议通读它以更好地理解所涉及的问题。

    ASP.NET MVC HandleError属性,自定义错误页面和日志记录异常
    (感谢下面的Scott Shepherd,他在下面的答案中提供了链接)。