2013-06-11 19 views
20

Wenn eine Ausnahme von Ihrem eigenen Code ausgelöst wird, der von einer Aktion in einem Controller aufgerufen wird, wie soll das gehandhabt werden? Ich sehe viele Beispiele für Best Practices, in denen es überhaupt keine Try-Catch-Statements gibt. Zum Beispiel den Zugriff auf Daten aus einem Repository:Ausnahmebehandlung in Controller (ASP.NET MVC)

public ViewResult Index() 
{ 
    IList<CustomModel> customModels = _customModelRepository.GetAll(); 
    return View(customModels); 
} 

Offensichtlich dieser Code eine Ausnahme auslösen könnte, wenn der Anruf zu einer Datenbank, die nicht darauf zugreifen können, und wir werden mit Hilfe eines ORM wie Entity Framework zum Beispiel.

Allerdings wird alles, was ich sehen kann, passieren, dass die Ausnahme platzt und dem Benutzer eine unangenehme Fehlermeldung anzeigt.

Ich kenne das HandleError-Attribut, aber ich verstehe, dass es meistens verwendet wird, um Sie auf eine Fehlerseite umzuleiten, wenn eine nicht behandelte Ausnahme auftritt.

Natürlich könnte dieser Code in einem Try-Catch gewickelt werden, sondern trennt sich nicht gut, vor allem, wenn Sie mehr Logik:

public ViewResult Index() 
{ 
    if (ValidationCheck()) 
    { 
     IList<CustomModel> customModels = new List<CustomModel>(); 
     try 
     { 
      customModels = _customModelRepository.GetAll(); 
     } 
     catch (SqlException ex) 
     { 
      // Handle exception 
     } 

     if (CustomModelsAreValid(customModels)) 
      // Do something 
     else 
      // Do something else 
    } 

    return View(); 
} 

Bisher habe ich alle Code extrahiert aus, die wie werfen könnten Ausnahmen Die Datenbank ruft eine DataProvider-Klasse auf, die Fehler verarbeitet und Nachrichten zurückgibt, um dem Benutzer Nachrichten anzuzeigen.

Ich fragte mich, was ist der beste Weg, dies zu handhaben? Ich möchte nicht immer zu einer Fehlerseite zurückkehren, da einige Ausnahmen dies nicht tun sollten. Stattdessen sollte eine Fehlermeldung für den Benutzer mit einer normalen Ansicht angezeigt werden. War meine bisherige Methode korrekt oder gibt es eine bessere Lösung?

Antwort

20

ich drei Dinge tun, um mehr benutzerfreundliche Meldungen angezeigt:

  1. Nutzen Sie die globalen Exception-Handler. Im Falle von MVC: Application_Error in Global.asax. Hier erfahren Sie, wie Sie es verwenden können: http://msdn.microsoft.com/en-us/library/24395wz3(v=vs.100).aspx
  2. Ich Unterklasse Exception in eine UserFriendlyException. Ich gebe mein Bestes in all meinen zugrunde liegenden Serviceklassen, um diese UserFriendlyException anstelle einer einfachen Ausnahme auszugeben. Ich versuche immer, benutzerbezogene Nachrichten in diesen benutzerdefinierten Ausnahmen zu speichern. Der Hauptzweck besteht darin, eine Typüberprüfung der Ausnahme in der Application_Error-Methode durchführen zu können. Für die UserFriendlyExceptions benutze ich einfach die benutzerfreundliche Nachricht, die ich tief in meinen Diensten eingegeben habe, wie "Hey! 91 Grad ist kein gültiger Breitenwert!". Wenn es eine reguläre Ausnahme ist, dann ist es ein Fall, den ich nicht behandelt habe, deshalb zeige ich eine allgemeinere Fehlermeldung, wie "Ups, etwas ist schief gelaufen! Wir werden unser Bestes tun, um das zu beheben!".
  3. Ich erstelle auch einen ErrorController, der für das Rendern benutzerfreundlicher Ansichten oder JSON verantwortlich ist. Dies ist der Controller, dessen Methoden von der Application_Error-Methode aufgerufen werden.

EDIT: Ich dachte, ich eine Erwähnung auf ASP.NET Web API geben würde, da es eng verwandt ist. Da der Endbenutzer von Web-API-Endpunkten nicht unbedingt ein Browser sein muss, möchte ich Fehler ein wenig anders behandeln. Ich benutze immer noch die "FriendlyException" (# 2 oben), aber statt auf einen ErrorController umzuleiten, lasse ich einfach alle meine Endpunkte einen Basistyp zurückgeben, der eine Error-Eigenschaft enthält. Wenn also eine Ausnahme bis zu den Web-API-Controllern durchgebrannt ist, halte ich diesen Fehler in der Error-Eigenschaft der API-Antwort fest. Diese Fehlermeldung ist entweder die freundliche Nachricht, die aus den Klassen, auf die der API-Controller angewiesen ist, aufgetaucht ist, oder eine generische Nachricht, wenn der Ausnahmetyp keine FriendlyException ist. Auf diese Weise kann der konsumierende Client einfach überprüfen, ob die Fehlereigenschaft der API-Antwort leer ist oder nicht. Zeigen Sie eine Nachricht an, wenn der Fehler vorhanden ist, fahren Sie wie üblich fort, wenn nicht. Das Schöne ist, dass die Nachricht aufgrund des freundlichen Nachrichtenkonzepts für den Benutzer viel aussagekräftiger sein kann als ein generischer "Fehler!" Botschaft. Ich verwende diese Strategie beim Schreiben von mobilen Apps mit Xamarin, wo ich meine C# -Typen zwischen meinen Webdiensten und meiner iOS/Android-App teilen kann.

+1

ich mit Patrick Desjardins' Antwort auch einig: zwingende OnException ist eine gute Möglichkeit, um Fehler zu behandeln. Vor allem, wenn es sich um einen Basiscontroller handelt, von dem alle anderen Controller geerbt werden. – NovaJoe

0

Alle Fragen wie diese sind nicht sehr konstruktiv, denn die Antwort ist immer "es kommt darauf an", denn es gibt so viele Möglichkeiten, mit der Fehlerbehandlung umzugehen.

Viele Leute verwenden gerne die HandleError-Methode, da jede Ausnahme grundsätzlich nicht wiederherstellbar ist. Ich meine, was wirst du tun, wenn du die Objekte nicht zurückgeben kannst? Du wirst ihnen trotzdem einen Fehler zeigen, oder?

Die Frage wird, wie Sie ihnen den Fehler zeigen möchten. Wenn es angezeigt wird, dass eine Fehlerseite angezeigt wird, funktioniert HandleError problemlos und bietet einen einfachen Ort, um den Fehler zu protokollieren. Wenn Sie Ajax verwenden oder etwas Zarteres wollen, müssen Sie einen Weg entwickeln, dies zu tun.

Sie sprechen über eine DataProvider-Klasse. Das ist im Grunde, was dein Repository ist. Warum nicht das in dein Repository einbauen?

16

Mit Asp.Net MVC können Sie auch die OnException-Methode für Ihren Controller überschreiben.

protected override void OnException(ExceptionContext filterContext) 
{ 
    if (filterContext.ExceptionHandled) 
    { 
     return; 
    } 
    filterContext.Result = new ViewResult 
    { 
     ViewName = ... 
    }; 
    filterContext.ExceptionHandled = true; 
} 

Dies ermöglicht es Ihnen, auf eine benutzerdefinierte Fehlerseite mit einer Nachricht, die auf die Ausnahme umleiten verweisen, wenn Sie wollen.

+0

Mai Downvoters argumentieren ein wenig: Was ist falsch mit dieser Antwort? – Askolein

+1

@Askolein Ich verstehe auch nicht. Sie können abstimmen, um es auf 0 zurückzusetzen, wenn Sie glauben, dass es eine mögliche Lösung ist, wie ich es tue. –

+0

und es ist getan ... +1 – Askolein

1

ich eine OnException Überschreibung verwendet, weil ich mehrere Projekte referenes auf eine haben, die einen Controller haben, die Fehler behandeln:

Sicherheit/HandleErrorsController.cs

protected override void OnException(ExceptionContext filterContext) 
{ 
    MyLogger.Error(filterContext.Exception); //method for log in EventViewer 

    if (filterContext.ExceptionHandled) 
     return; 

    filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError; 

    filterContext.Result = new JsonResult 
    { 
     Data = new 
     { 
      Success = false, 
      Error = "Please report to admin.", 
      ErrorText = filterContext.Exception.Message, 
      Stack = filterContext.Exception.StackTrace 
     }, 
     JsonRequestBehavior = JsonRequestBehavior.AllowGet 
    }; 
    filterContext.ExceptionHandled = true; 
} 
+2

Es ist eine wirklich schlechte Idee, rohe Ausnahmeinhalte wie Nachrichten und Stack-Traces an den Benutzer zurückzugeben. –