2012-02-27 31 views
10

Tengo un atributo personalizado HandleError que se ocupa de los errores en la canalización de MVC; Tengo un método protected void Application_Error(object sender, EventArgs e) en mi Global.asax que maneja los errores desde fuera de la tubería.ASP.NET MVC Global error handling

Me he encontrado con un escenario que no sabía que era posible; Al implementar DI, existe una dependencia para connectionString, que se toma del archivo de configuración de la aplicación.

Como no existía la cadena de conexión, sin embargo, plantea un error al crear el controlador, esto por lo general hace que el fuego Application_Error manipulador, y una página de error adecuado se vuelve (a través de la prestación de una vista parcial como cadena y enviándolo como el "excepción grave".. la respuesta, y en caso de que esto no sólo se escribe en la respuesta

Salvo en este caso, me sale el ASP.NET "error de ejecución" fugly por defecto pantalla amarilla de la muerte me dice:

Runtime Error

Descripción: Se ha producido un error de aplicación en el servidor . La configuración de error personalizado actual para esta aplicación evita que se vean los detalles del error de la aplicación.

Detalles: Para habilitar los detalles de este mensaje de error específico para ser visible en la máquina servidor local, por favor crear una etiqueta dentro de un archivo de configuración "web.config" ubicado en el directorio raíz de la aplicación web actual . Esta etiqueta debe tener su atributo "modo" establecido en "RemoteOnly". Para habilitar los detalles para que se puedan ver en máquinas remotas, configure "modo" en "Desactivado".

que no tienen un conjunto defaultRedirect en mi customErrors, ni es Off volvió, porque no quiero volver a dirigir, sino para hacer que los errores en la misma página que el usuario se encuentra, evitando una redirección innecesaria.

¿Cómo puedo manejar un escenario como este? ¿Y cuál es la razón por la que se comporta de esta manera y no como cualquier otro error fuera de un controlador?

Me doy cuenta de que no es probable que suceda a menudo, pero me gustaría poder detener el YSOD (en parte porque quiero ocultar la tecnología que estoy usando, pero sobre todo porque no es bonita ni amigable)

Incluso intenté registrar un controlador para UnhandledExceptions, pero tampoco se activó.

AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; 

El código que en última instancia produce esto, es:

return ConfigurationManager.ConnectionStrings[key].ConnectionString;, donde ConnectionStrings[key] es null.

actualización

Esto es cómo se manejan los errores applicaion:

protected void Application_Error(object sender, EventArgs e) 
    { 
     this.HandleApplicationError(new ResourceController()); 
    } 

    public static void HandleApplicationError(this HttpApplication application, BaseController controller) 
    { 
     if (application == null) 
     { 
      throw new ArgumentNullException("application"); 
     } 
     if (controller == null) 
     { 
      throw new ArgumentNullException("controller"); 
     } 
     application.Response.Clear(); 
     Exception exception = application.Server.GetLastError(); 
     LogApplicationException(application.Response, exception); 
     try 
     { 
      RenderExceptionViewResponse(application, exception, controller); 
     } 
     catch (Exception exceptionRenderingView) // now we're in trouble. let's be as graceful as possible. 
     { 
      RenderExceptionTextResponse(application, exceptionRenderingView); 
     } 
     finally 
     { 
      application.Server.ClearError(); 
     } 
    } 

    private static void LogApplicationException(HttpResponse response, Exception exception) 
    { 
     if (exception is HttpException) 
     { 
      HttpException httpException = (HttpException)exception; 
      if (httpException.GetHttpCode() == (int)HttpStatusCode.NotFound) 
      { 
       _log.Debug(Resources.Error.WebResourceNotFound, httpException); 
       response.Status = Resources.Constants.NotFound; 
       return; 
      } 
     } 
     _log.Error(Resources.Error.UnhandledException, exception); 
    } 

    private static void RenderExceptionViewResponse(HttpApplication application, Exception exception, BaseController controller) 
    { 
     if (!RenderAsJsonResponse(application, Resources.User.UnhandledExceptionJson)) 
     { 
      ErrorViewModel model = WebUtility.GetErrorViewModel(exception); 
      string result = controller.RenderViewToString(Resources.Constants.ErrorViewName, model); 
      application.Response.Write(result); 
     } 
    } 

    private static void RenderExceptionTextResponse(HttpApplication application, Exception exceptionRenderingView) 
    { 
     application.Response.Clear(); 

     if (!RenderAsJsonResponse(application, Resources.User.FatalExceptionJson)) 
     { 
      application.Response.Write(Resources.User.FatalException); 
     } 
     _log.Fatal(Resources.Error.FatalException, exceptionRenderingView); 
    } 

    private static bool RenderAsJsonResponse(HttpApplication application, string message) 
    { 
     if (application.Request.IsAjaxRequest()) 
     { 
      application.Response.Status = Resources.Constants.HttpSuccess; 
      application.Response.ContentType = Resources.Constants.JsonContentType; 
      application.Response.Write(message); 
      return true; 
     } 
     return false; 
    } 

Este es el atributo que utilizo para decorar mi base:

public class ErrorHandlingAttribute : HandleErrorAttribute 
{ 
    public Type LoggerType { get; set; } 

    public ErrorHandlingAttribute() 
     : this(typeof(ErrorHandlingAttribute)) 
    { 
    } 

    public ErrorHandlingAttribute(Type loggerType) 
    { 
     LoggerType = loggerType; 
    } 

    public override void OnException(ExceptionContext filterContext) 
    { 
     if (filterContext.ExceptionHandled) 
     { 
      return; 
     } 
     if (filterContext.HttpContext.Request.IsAjaxRequest()) 
     { 
      OnAjaxException(filterContext); 
     } 
     else 
     { 
      OnRegularException(filterContext); 
     } 
    } 

    internal protected void OnRegularException(ExceptionContext filterContext) 
    { 
     Exception exception = filterContext.Exception; 

     ILog logger = LogManager.GetLogger(LoggerType); 
     logger.Error(Resources.Error.UnhandledException, exception); 

     filterContext.HttpContext.Response.Clear(); 

     ErrorViewModel model = WebUtility.GetErrorViewModel(exception); 
     filterContext.Result = new ViewResult 
     { 
      ViewName = Resources.Constants.ErrorViewName, 
      ViewData = new ViewDataDictionary(model) 
     }; 
     filterContext.ExceptionHandled = true; 
    } 

    internal protected void OnAjaxException(ExceptionContext filterContext) 
    { 
     Exception exception = filterContext.Exception; 

     ILog logger = LogManager.GetLogger(LoggerType); 
     logger.Error(Resources.Error.UnhandledAjaxException, exception); 

     filterContext.HttpContext.Response.Clear(); 
     filterContext.HttpContext.Response.Status = Resources.Constants.HttpSuccess; 

     string errorMessage = WebUtility.GetUserExceptionMessage(exception, true); 

     filterContext.Result = new ExceptionJsonResult(new[] { errorMessage }); 
     filterContext.ExceptionHandled = true; 
    } 
} 

Y esta es mi customErrors :

<customErrors mode="On" /> 

Como puede ver, estos son bastante extensos, sin embargo, ni siquiera se disparan en el caso de acceder al ConnectionStrings donde el ConnectionString no existe; que es un poco desconcertante.

hace incendio en cualquier controlador contenía excepciones o excepciones que no están dentro de un controlador, por lo que no entiendo por qué este caso es diferente.

Respuesta

0

No ha mostrado exactamente cómo maneja los errores en su evento Application_Error ni cómo se implementa su HandleAttribute personalizado, por lo que es difícil adivinar cuál podría ser el problema. Un problema común a tener en cuenta cuando se implementa Application_Error es que se genera una vista de error que arroja un error. Piénsese, por ejemplo, en una vista de error que depende de un Diseño y dentro de este diseño está llamando a un ayudante Html.Action para representar el contenido de otra acción que sí tiene acceso a la base de datos y demás. Sus vistas de error deben ser lo más estáticas posible. Lo ideal es que tengas un diseño diferente para evitar ese tipo de situaciones.

También puedo sugerir que eche un vistazo al following approach para el manejo de errores.

+0

He actualizado con mi configuración de manejo de errores. – bevacqua

+0

¿Qué hay de usar PartialView? ¿Podría echar un vistazo a [Manejo de errores globales con PartialView en MVC] (http://stackoverflow.com/questions/39594409/global-error-handling-using-partialview-in-mvc)? –

7

Application_Error podría ser la forma recomendada de manejar errores en ASP.NET WebForms. Pero no en MVC.

Recibimos los filtros de error que se encargan de los errores para nosotros. El problema con el filtro es que solo funciona cuando se ha invocado un controlador. ¿Cuál es el problema de los errores 404 y 401 (no encontrado y autorización) y el problema de conexión de su base de datos?

customErrors es el camino a seguir aquí. No veo por qué la redirección debería ser un problema.

Voy a través de manejo de errores adecuado en un artículo en el blog: http://blog.gauffin.org/2011/11/how-to-handle-errors-in-asp-net-mvc/

+8

Redirigir los errores personalizados es un problema porque básicamente le dice al navegador (o bot) la página que falta "sí esta página está bien, pero por favor visite esta otra página por ahora", luego la siguiente página dice "no yo" No estoy bien, no existo ". Sin embargo, su navegador y el bot (por ejemplo, google bot) almacenarán la página que falta como página real y continuarán intentándolo e indexándolo en el caso de Google, porque una redirección temporal indica que seguirá revisando la página original en el futuro. Por eso es muy importante devolver el estado 404 en la URL original solicitada. –

+0

@jgauffin El redireccionamiento también es un problema cuando se prefiere utilizar ** PartialView ** para el manejo de errores y excepciones. ¿Podría echar un vistazo a [Manejo de errores globales con PartialView en MVC] (http://stackoverflow.com/questions/39594409/global-error-handling-using-partialview-in-mvc)? –

0

ser correcta a nuestros visitantes y los motores de búsqueda, que debe devolver los códigos de estado HTTP significativas si se producen errores, frente a nuestro sitio. No es justo devolver el código de estado HTTP 200 en caso de error, incluso si al mismo tiempo devolvemos una vista, explicando que ocurrió un error. si el usuario ingresa una dirección incorrecta (la falla de usuario más frecuente), debemos devolver el código de estado HTTP 404 y no devolver o redireccionar a una Vista, donde se devolverá el código de estado 200.
Hay un breve y limpio resumen de recomendaciones HERE.