2009-06-23 15 views
22

Szukam prostego rozwiązania do wykonywania rejestrowania wyjątków w połączeniu z obsługą błędów w mojej aplikacji ASP.Net MVC 1.0.Wywołanie wyjątku ASP.Net MVC w połączeniu z obsługą błędów

Przeczytałem wiele artykułów, w tym zamieszczono tutaj pytania dotyczące StackOverflow, które zapewniają różne rozwiązania dla różnych sytuacji. Nadal nie jestem w stanie zaproponować rozwiązania, które odpowiada moim potrzebom.

Oto moje wymagania:

  1. Aby móc korzystać z [HandleError] atrybut (lub jego odpowiednik) w moim kontrolera, aby obsłużyć wszystkie wyjątki, które mogą zostać wyrzucone z któregokolwiek z działań lub Wyświetleń . Powinno to obsłużyć wszystkie wyjątki, które nie były obsługiwane w żadnej z Akcji (jak opisano w punkcie 2). Chciałbym móc określić, do którego widoku należy przekierować użytkownika, w przypadkach błędów, dla wszystkich działań w kontrolerze.

  2. Chcę móc określić atrybut [HandleError] (lub coś podobnego) u góry określonych akcji, aby wychwycić określone wyjątki i przekierować użytkowników do widoku odpowiedniego dla wyjątku. Wszystkie inne wyjątki muszą być nadal obsługiwane przez atrybut [HandleError] na kontrolerze.

  3. W obu powyższych przypadkach chcę, aby wyjątki były rejestrowane za pomocą log4net (lub dowolnej innej biblioteki rejestrowania).

Jak mogę osiągnąć powyższe cele? Czytałem o tym, aby wszystkie moje kontrolery dziedziczyły z kontrolera bazowego, który nadpisuje metodę OnException, i w której robię logowanie. Będzie to jednak kłopotliwe z przekierowaniem użytkowników do odpowiednich widoków lub sprawi, że będzie bałagan.

Przeczytałem o pisaniu własnej akcji filtrowania, która implementuje IExceptionFilter, aby poradzić sobie z tym, ale będzie to sprzeczne z atrybutem [HandleError].

Do tej pory uważam, że najlepszym rozwiązaniem jest napisanie własnego atrybutu, który dziedziczy z HandleErrorAttribute. W ten sposób otrzymam całą funkcjonalność [HandleError] i mogę dodać własne logowanie log4net. Rozwiązanie jest następujące:

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); 
     } 
    } 

Czy powyższy kod będzie działał zgodnie z moimi wymaganiami? Jeśli nie, jakie rozwiązanie spełnia moje wymagania?

Odpowiedz

24

nadal jestem nieco mylić z wszystkich różnych rozwiązań tam, jak i atrybuty mogą kolidować ze sobą, ale poszedłem z tego rozwiązania:

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 
} 

ja nadal używać [HandleError] atrybut jak wyjaśniono w pierwotnym pytaniu, a ja po prostu dekoruję każdego kontrolera za pomocą atrybutu [LogErrors].

To działa dla mnie, ponieważ utrzymuje błąd logowania w jednym miejscu i nie powoduje wielokrotnego rejestrowania zduplikowanych wyjątków (co stanie się, jeśli rozszerzę [HandleError] i użyję tego atrybutu w wielu miejscach).

nie sądzę, będzie można połączyć zarówno rejestrowanie wyjątków i obsługa błędów w jednym atrribute lub klasy, bez niego staje się bardzo uciążliwe i skomplikowane, ani wpływu na stosowanie [HandleError]

Ale to działa dla mnie, ponieważ dekoruję każdego kontrolera tylko raz, za pomocą atrybutu [LogErrors] i dekoruję Kontrolery i Akcje za pomocą [HandleError] dokładnie tak, jak chcę, bez wzajemnego przeszkadzania sobie.

Aktualizacja:

Oto przykład, jak go używać:

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

    } 
} 

W powyższym kodzie, SmtpExceptions w przeciążeniem ContactForm działania są obsługiwane w bardzo specyficzny sposób - użytkownik jest prezentowany z właściwością ViewPage dla wiadomości, które nie zostały wysłane, w tym przypadku nazywa się "MessageFailed". Wszystkie inne wyjątki są obsługiwane przez domyślne zachowanie [HandleError]. Należy również pamiętać, że rejestrowanie błędów występuje najpierw, a następnie obsługa błędów. Jest to wskazane brzmienie:

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

Aktualizacja:

Istnieje alternatywne rozwiązanie do tego, z bardzo dobrym explanantion. Polecam lekturę, aby lepiej zrozumieć związane z tym problemy.

ASP.NET MVC HandleError Attribute, Custom Error Pages and Logging Exceptions (Podziękowania dla Scotta Shepherda poniżej, który podał link w odpowiedzi poniżej).

+0

Miło, dziękuję bardzo, wykorzystam to. – Kezzer

+3

Podejrzewam, że ten kod ma błąd, w którym zakłada, że ​​null nie jest zwracany dla RouteData.Values ​​["action"] - wywołanie. ToString() może spowodować, że twój uchwyt błędu wyrzuci wyjątek NullReferenceException. Nic nie jest bardziej frustrujące niż obsługa błędów zgłaszająca błąd. –

Powiązane problemy