18

Ho la seguente nel mio web.config:In ASP.NET MVC qual è il migliore spettacolo eccezioni non gestite nella mia vista?

<customErrors mode="On" defaultRedirect="Error"> 
    <error statusCode="404" redirect="Error/NotFound" /> 
</customErrors> 

Ho un

[HandleError] 

in cima alla mia classe HomeController. Per testare, creo e azione che genera semplicemente un'eccezione. . e reindirizza a mio metodo

ErrorController/Index 

ma quando si arriva alla mia vista, che si lega al HandleErrorInfo mio modello è nullo quindi in qualche modo perso il riferimento all'errore.

Sono sicuro che ha qualcosa a che fare con l'errore perdersi nel reindirizzamento, quindi volevo vedere se mi mancava qualcosa e se qualcuno aveva suggerimenti su dove posso avere una vista che mostra Stacktrace e un messaggio di errore.

+0

customError perde tutto il riferimento all'errore. –

+0

possibile duplicato di [Come gestisco le eccezioni non rilevate in un'applicazione ASP.NET MVC 3?] (Http://stackoverflow.com/questions/6596648/how-do-i-handle-uncaught-exceptions-in-an- asp-net-mvc-3-application) –

+0

Mentre customError reindirizza sta facendo una nuova richiesta http in modo da perdere tutti i precedenti dati di risposta. Quello di cui hai bisogno è impostare la modalità redirectMode su ResponseRewrite in modo da non fare una nuova richiesta. –

risposta

22

Posso vedere l'equivoco. Si desidera eseguire la cosa MVC e redirect su un'azione del controller.

Ma defaultRedirect è di per sé una convenzione Web Form e pertanto limitata. Nel momento in cui si reindirizza ad un altro controller, si perde il vostro HttpContext, e quindi perdere la HandleErrorInfo oggetto

tuo [HandleError] attributo richiede un View per dirigere il suo messaggio di errore. Seguendo il tuo esempio sopra, suppongo tu abbia una cartella Views/Error per il tuo ErrorController, e in essa hai una vista Index. Se si vuole il vostro filtro di contesto per inviare un oggetto HandleErrorInfo a quella vista,

Prova questa sintassi:

[HandleError(View="~/Views/Error/Index")] 
Public class HomeController : Controller 

Ma per quanto riguarda la registrazione?!?!?

ho il sospetto la vostra intenzione è più che visualizzare stack errori per gli utenti. In realtà, sospetto che tu non abbia affatto tale intenzione. Sospetto che il tuo vero scopo sia quello di registrare il tuo errore (probabilmente in db) e di mostrare un messaggio blando al tuo utente.

Quello che ho spiegato fino ad ora è stato "quale è il migliore [modo per] mostrare le eccezioni non gestite nella mia vista". L'attributo [HandleError] è buono per quello.

Ma quando si vuole passare alla fase successiva (registrazione di errore) si dispone di alcune opzioni:

1) Override your base controller's On Exception method; crea il tuo Controller ereditato dalla classe MVC Controller ma sostituisci il metodo On Exception. Questo approccio può essere utilizzato insieme all'attributo [HandleError]

2) Create a custom exception handler Creare il proprio gestore di eccezioni che registra l'errore. Il gestore delle eccezioni può quindi chiamare una vista di scelta o può lavorare in congiunzione con [HandleError(order=2)] poiché gli attributi del filtro possono accettare un argomento ordine che applica la precedenza.


Nitin Sawant chiede cosa una vista di errore sarà simile. The

@model System.Web.Mvc.HandleErrorInfo 
<h2>Exception details</h2> 
<p> Controller: @Model.ControllerName </p> 
<p> Action: @Model.ActionName </p> 
<p> Exception: @Model.Exception </p> 
+0

puoi pubblicare i contenuti di ~ ~/Views/Error/Index'? –

+1

@NitinSawant, spero che questo aiuti –

1

Ho usato questo piccolo pezzo di codice per mostrare la pagina degli errori gestiti dagli utenti. Se la pagina non è stata trovata o si è verificato un altro errore.

void Application_Error(object sender, EventArgs e) 
    { 
     // this value can be fetched from config or depend on DEBUG smybol 
     if (!handleErrors) 
      return; 

     var error = Server.GetLastError(); 
     var code = (error is HttpException) ? (error as HttpException).GetHttpCode() : 500; 

     if (code == 404) 
     { 
      // do something if page was not found. log for instance 
     } 
     else 
     { 
      // collect request info and log exception 
     } 

     // pass exception to ErrorsController 
     Request.RequestContext.RouteData.Values["ex"] = error; 

     // execute controller action 
     IController errorController = new ErrorsController(); 
     errorController.Execute(new RequestContext(new HttpContextWrapper(Context), Request.RequestContext.RouteData)); 
    } 

E il controller degli errori è simile a questo. Se è necessaria un'eccezione dettagliata, è accessibile tramite RouteData

public class ErrorsController : Controller 
{ 
    /// <summary> 
    /// Page not found 
    /// </summary> 
    /// <returns></returns> 
    public ActionResult Http404() 
    { 
     return View(); 
    } 

    /// <summary> 
    /// All other errors 
    /// </summary> 
    /// <param name="actionName"></param> 
    protected override void HandleUnknownAction(string actionName) 
    { 
     // in case detailed exception is required. 
     var ex = (Exception) RouteData.Values["ex"]; 
     return View(); 
    } 
} 

È possibile aggiungere una vista diversa per ciascun codice http. Basta implementare l'azione Http {Code}

6

Faccio qualcosa di simile a maxlego che gestisce tutti gli errori (non solo quelli che si verificano nei controller con l'attributo HandleError).

La mia classe MvcApplication (in global.asax.cs) ha questo:

public class MvcApplication : HttpApplication 
{ 
    // usual stuff here... 

    protected void Application_Error(object sender, EventArgs e) 
    { 
     Server.HandleError(((MvcApplication)sender).Context); 
    } 
} 

Il codice precedente utilizza un metodo di estensione dalla mia biblioteca MVC di cose utili. Con questo in atto non ho bisogno di alcun errore nella gestione degli attributi, customErrors config o filtri personalizzati. Invece il metodo di estensione registrerà i dettagli dell'errore quindi richiamare una vista appropriata, sia:

  • AccessDenied
  • NOTFOUND
  • InternalServerError

Il codice metodo di estensione per fare questo lavoro è:

public static class HttpServerUtilityExtensions 
{ 
    private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); 

    public static void HandleError(this HttpServerUtility server, HttpContext httpContext) 
    { 
     var currentController = " "; 
     var currentAction = " "; 
     var currentRouteData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext)); 

     if (currentRouteData != null) 
     { 
      if (currentRouteData.Values["controller"] != null && !String.IsNullOrEmpty(currentRouteData.Values["controller"].ToString())) 
       currentController = currentRouteData.Values["controller"].ToString(); 

      if (currentRouteData.Values["action"] != null && !String.IsNullOrEmpty(currentRouteData.Values["action"].ToString())) 
       currentAction = currentRouteData.Values["action"].ToString(); 
     } 

     var exception = server.GetLastError(); 
     Logger.ErrorException(exception.Message, exception); 

     var controller = DependencyResolver.Current.GetService<ErrorController>(); 
     var routeData = new RouteData(); 
     var action = "InternalServerError"; 

     if (exception is HttpException) 
     { 
      var httpEx = exception as HttpException; 

      switch (httpEx.GetHttpCode()) 
      { 
       case 404: 
        action = "NotFound"; 
        break; 

       case 401: 
        action = "AccessDenied"; 
        break; 
      } 
     } 

     httpContext.ClearError(); 
     httpContext.Response.Clear(); 
     httpContext.Response.StatusCode = exception is HttpException ? ((HttpException)exception).GetHttpCode() : 500; 
     httpContext.Response.TrySkipIisCustomErrors = true; 

     routeData.Values["controller"] = "Error"; 
     routeData.Values["action"] = action; 

     controller.ViewData.Model = new HandleErrorInfo(exception, currentController, currentAction); 
     ((IController)controller).Execute(new RequestContext(new HttpContextWrapper(httpContext), routeData)); 
    } 
} 

Nota: quanto sopra utilizza NLog per registrare i dettagli dell'errore, ma potrebbe facilitare essere cambiato per supportare qualcos'altro. Inoltre, questo metodo rispetta il contenitore IoC durante la risoluzione di ErrorController.

Problemi correlati