Протоколирование исключений ASP.Net MVC в сочетании с обработкой ошибок - PullRequest
22 голосов
/ 23 июня 2009

Я ищу простое решение для ведения журнала исключений в сочетании с обработкой ошибок в моем приложении ASP.Net MVC 1.0.

Я прочитал много статей, в том числе Вопросы, размещенные здесь в StackOverflow, которые предоставляют различные решения для разных ситуаций. Я все еще не могу найти решение, которое соответствует моим потребностям.

Вот мои требования:

  1. Чтобы иметь возможность использовать атрибут [HandleError] (или что-то эквивалентное) на моем контроллере, для обработки всех исключений, которые могут быть выброшены из любого действия или представления. Это должно обрабатывать все исключения, которые не были обработаны специально ни для одного из действий (как описано в пункте 2). Я хотел бы иметь возможность указать, к какому представлению пользователь должен перенаправляться в случае ошибок, для всех действий в контроллере.

  2. Я хочу иметь возможность указать атрибут [HandleError] (или что-то эквивалентное) в верхней части определенных действий, чтобы перехватывать определенные исключения и перенаправлять пользователей в представление, соответствующее исключению. Все остальные исключения по-прежнему должны обрабатываться атрибутом [HandleError] на контроллере.

  3. В обоих вышеупомянутых случаях я хочу, чтобы исключения регистрировались с использованием log4net (или любой другой библиотеки журналов).

Как мне достичь вышеуказанного? Я читал о том, как заставить все мои контроллеры наследовать от базового контроллера, который переопределяет метод OnException, и в котором я веду свою регистрацию. Однако это может привести к перенаправлению пользователей в соответствующие представления или усложнить работу.

Я читал о написании моего собственного действия фильтра, которое реализует IExceptionFilter для обработки этого, но это будет конфликтовать с атрибутом [HandleError].

Пока что я думаю, что лучшее решение - написать свой собственный атрибут, который наследуется от HandleErrorAttribute. Таким образом, я получаю всю функциональность [HandleError] и могу добавить свою собственную регистрацию в log4net. Решение заключается в следующем:

    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 Ответ

25 голосов
/ 24 июня 2009

Я все еще немного запутался во всех различных решениях и в том, как атрибуты могут мешать друг другу, но я выбрал это решение:

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] именно так, как я хочу, без того, чтобы они мешали друг другу.

Обновление:

Вот пример того, как я его использую:

[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"; } }

    }
}

В приведенном выше коде SmtpExceptions в перегрузке действия ContactForm обрабатываются очень специфическим способом - пользователю предоставляется ViewPage, специфичный для отправленных сообщений с ошибками, в данном случае он называется «MessageFailed». Все остальные исключения обрабатываются поведением по умолчанию [HandleError]. Также обратите внимание, что сначала происходит регистрация ошибок, а затем обработка ошибок. На это указывает следующее:

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

Обновление:

Существует альтернативное решение с очень хорошим объяснением. Я рекомендую прочитать его, чтобы лучше понять связанные с этим вопросы.

Атрибут HandleError ASP.NET MVC, пользовательские страницы ошибок и исключения журналирования (Спасибо Скотту Шепарду ниже, который предоставил ссылку в ответе ниже).

...