2013-06-11 25 views
20

Quando un'eccezione viene generata dal proprio codice richiamato da un'azione in un controller come dovrebbe essere gestito? Vedo un sacco di esempi di buone pratiche in cui non esistono affatto dichiarazioni di prova. Ad esempio, l'accesso ai dati da un repository:Gestione delle eccezioni nel controller (ASP.NET MVC)

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

Chiaramente questo codice potrebbe gettare un'eccezione se la chiamata è a un database che non può accedere e che stiamo usando un ORM come Entity Framework, per esempio.

Tuttavia, tutto quello che posso vedere succederà è che l'eccezione verrà visualizzata e mostrerà un brutto messaggio di errore all'utente.

Sono a conoscenza dell'attributo HandleError, ma ho capito che è principalmente utilizzato per reindirizzare a una pagina di errore se si verifica un'eccezione non gestita.

Naturalmente, questo codice potrebbe essere avvolto in un try-catch, ma non separa bene, soprattutto se si dispone di più logica:

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

In precedenza ho estratto fuori tutto il codice che potrebbe generare eccezioni come chiamate di database in una classe DataProvider che gestisce gli errori e restituisce i messaggi per la visualizzazione dei messaggi all'utente.

Mi chiedevo quale sia il modo migliore per gestirlo? Non voglio sempre tornare a una pagina di errore perché alcune eccezioni non dovrebbero farlo. Invece, un messaggio di errore per l'utente dovrebbe essere visualizzato con una vista normale. Il mio metodo precedente era corretto o esiste una soluzione migliore?

risposta

20

devo fare tre cose per visualizzare più messaggi user-friendly:

  1. Approfittate del gestore di eccezioni globale. Nel caso di MVC: Application_Error in Global.asax. Scopri come utilizzarlo qui: http://msdn.microsoft.com/en-us/library/24395wz3(v=vs.100).aspx
  2. I sottoclasse eccezione in UserFriendlyException. Faccio del mio meglio in tutte le mie sottostanti classi di servizio per lanciare questa UserFriendlyException invece di una semplice vecchia eccezione. Cerco sempre di mettere messaggi significativi per l'utente in queste eccezioni personalizzate. Lo scopo principale del quale è essere in grado di eseguire un controllo del tipo sull'eccezione nel metodo Application_Error. Per UserFriendlyExceptions, utilizzo solo il messaggio user-friendly che ho impostato in profondità nei miei servizi, ad esempio "Hey! 91 gradi non è un valore di latitudine valido!". Se si tratta di un'eccezione normale, allora è un caso che non ho gestito, quindi visualizzo un messaggio di errore più generico, come "Oops, qualcosa è andato storto! Faremo del nostro meglio per ottenere quello corretto!".
  3. Inoltre creo un ErrorController che è responsabile del rendering di viste o JSON user-friendly. Questo è il controller i cui metodi verranno chiamati dal metodo Application_Error.

EDIT: ho pensato di dare una menzione ad ASP.NET Web API dal momento che è strettamente legato. Poiché il consumatore degli endpoint dell'API Web non sarà necessariamente un browser, mi piace gestire gli errori in modo leggermente diverso. Uso ancora il "FriendlyException" (n. 2 sopra), ma invece di reindirizzare ad un ErrorController, lascio semplicemente che tutti i miei endpoint restituiscano un tipo di tipo base che contiene una proprietà Error. Pertanto, se un'eccezione è presente fino ai controller API Web, mi assicuro di mantenere questo errore nella proprietà Error della risposta API. Questo messaggio di errore può essere il messaggio amichevole che ha preso il sopravvento dalle classi su cui fa affidamento il controller API, oppure sarà un messaggio generico se il tipo di eccezione non è un FriendlyException. In questo modo, il client che consuma può semplicemente verificare se la proprietà Error della risposta dell'API è vuota. Visualizza un messaggio se l'errore è presente, procedi come al solito in caso contrario. La cosa bella è che, a causa del concetto di messaggio amichevole, il messaggio potrebbe essere molto più significativo per l'utente di un generico "Errore!" Messaggio. Io uso questa strategia quando scrivo app per dispositivi mobili con Xamarin, dove posso condividere i miei tipi di C# tra i miei servizi web e la mia app iOS/Android.

+1

Sono anche d'accordo con la risposta Patrick Desjardins': l'override OnException è un ottimo modo per gestire gli errori. Soprattutto se si trova in un controller di base ereditato da tutti gli altri controller. – NovaJoe

0

Tutte le domande come questa non sono molto costruttive, perché la risposta è sempre "dipende", perché ci sono tanti modi per gestire la gestione degli errori.

A molte persone piace utilizzare il metodo HandleError, perché qualsiasi eccezione è fondamentalmente non recuperabile. Voglio dire, cosa hai intenzione di fare se non puoi restituire gli oggetti? Stai per mostrare loro comunque un errore, giusto?

La domanda diventa, come si desidera mostrare loro l'errore. Se mostrare loro una pagina di errore è accettabile, HandleError funziona correttamente e fornisce un posto facile per registrare l'errore. Se stai usando Ajax o vuoi qualcosa di più divertente, allora devi sviluppare un modo per farlo.

Si parla di una classe DataProvider. Questo è fondamentalmente il tuo repository. Perché non costruirlo nel tuo repository?

16

Con Asp.Net MVC è anche possibile eseguire l'override del metodo OnException per il controller.

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

Questo permette di reindirizzare a una pagina di errore personalizzata con un messaggio che si riferiscono a l'eccezione, se si vuole.

+0

Forse i downvoters discutono un po ': cosa c'è di sbagliato in questa risposta? – Askolein

+1

@Askolein Non capisco neanche. Puoi votare per riportarlo a 0 se ritieni che sia una soluzione possibile come faccio io. –

+0

ed è fatto ... +1 – Askolein

1

Ho usato un override OnException perché ho diversi progetti referenes a uno che dispone di un controller che gestisce gli errori:

Sicurezza/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

È una pessima idea restituire all'utente i contenuti delle eccezioni come i messaggi e le tracce dello stack. –