c# - net - global asax application_error
Manejo de error global de ASP.NET MVC (3)
Application_Error
podría ser la forma recomendada de manejar errores en ASP.NET WebForms. Pero no en MVC.
Tenemos 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.
Estoy pasando por el manejo correcto de errores en un artículo de blog: http://blog.gauffin.org/2011/11/how-to-handle-errors-in-asp-net-mvc/
Tengo un atributo HandleError
personalizado que trata 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.
Me encontré con un escenario que no sabía que fuera posible; Al implementar DI, existe una dependencia para un connectionString
, que se toma del archivo de configuración de la aplicación.
Como la cadena de conexión aún no existía, se produce un error al crear el controlador, esto generalmente hace que el controlador Application_Error
active y se muestre una página de error adecuada (al representar una vista parcial como cadena y enviarla como respuesta, y caso de que esto falle, simplemente escribe "Excepción fatal" en la respuesta.
Excepto en este caso, aparece la pantalla amarilla de la muerte de ASP.NET fugly default "error de tiempo de ejecución". Diciendome:
Error de tiempo de ejecución
Descripción: se produjo 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 permitir que los detalles de este mensaje de error específico se puedan ver en la máquina del servidor local, cree 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 permitir que los detalles se puedan ver en máquinas remotas, configure "modo" en "Desactivado".
No tengo un conjunto de defaultRedirect
en mis customErrors
, ni está Off
, porque no quiero redireccionar sino representar los errores en la misma página en la que se encuentra el usuario, 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 ocurra 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 fácil de usar)
Incluso traté de registrar un controlador para Excepción no controlada, pero tampoco se activó.
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
El código que finalmente produce esto, es:
return ConfigurationManager.ConnectionStrings[key].ConnectionString;
, donde ConnectionStrings[key]
es null
.
Actualizar
Así es como se manejan los errores de aplicación:
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 uso para decorar mi controlador 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 este es mi customErrors
:
<customErrors mode="On" />
Como puede ver, estos son bastante extensos, sin embargo, ni siquiera se activan en el caso de acceder a ConnectionStrings
donde ConnectionString
no existe; que es un poco desconcertante.
Dispara en cualquier controlador que contenga excepciones o excepciones que no estén dentro de un controlador, por lo que no entiendo por qué este caso es diferente.
Para ser correctos con nuestros visitantes y los motores de búsqueda, debemos devolver códigos de estado HTTP significativos 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 AQUÍ .
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. Piense, 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í mismo tiene acceso a la base de datos y esas cosas. 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 sugerirle que eche un vistazo al siguiente enfoque para manejar errores.