asp.net - page - MVC[HandleError] HandleErrorAttribute llamado dos veces cuando se usa el registro global
mvc try catch (4)
Podría crear un IFilterProvider
personalizado que verificará si el filtro ya se ha aplicado a esa acción:
public class MyFilterProvider : IFilterProvider
{
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
if (!actionDescriptor.GetFilterAttributes(true).Any(a => a.GetType() == typeof(MyHandleErrorAttribute)))
{
yield return new Filter(new MyHandleErrorAttribute(), FilterScope.Global, null);
}
}
}
Luego, en lugar de registrar su filtro con GlobalFilterCollection
, registraría su proveedor de filtros en Application_Start()
FilterProviders.Providers.Add(new MyFilterProvider());
Alternativamente (similar a lo que @Mark propuso) usted podría establecer explícitamente la propiedad ExceptionContext
de ExceptionContext
public class MyHandleErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
if(context.ExceptionHandled) return;
// Write to log code
base.OnException(context);
context.ExceptionHandled = true;
}
}
En una aplicación web MVC3 que estaba usando
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
para aplicar el manejo de error global donde se le mostró al usuario la vista ''Error'' si ocurría una excepción no controlada.
Para una vista particular, también quería que se mostrara una vista de error diferente si ocurría una excepción no controlada decorando el método con [HandleError(View = "SpecialError")]
. Esto funcionó bien.
Luego quise agregar el registro global de excepciones no controladas. Creé un atributo HandleError personalizado con código de registro:
public class MyHandleErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
// Write to log code
base.OnException(context);
}
}
Y actualice RegisterGlobalFilters y la decoración del método para usar este nombre de atributo en su lugar. Esto funciona en general, pero cuando ocurre una excepción dentro del método que está decorado con MyHandleError(View = "SpecialError")]
el método OnException se llama dos veces . Originalmente asumí que decorar el método con este atributo reemplazó al controlador global, pero parece que simplemente se agrega (lo cual tiene más sentido, pero no es lo que quiero). Al invocar OnException dos veces, la misma excepción se registra dos veces, lo que no debe suceder. No creo que OnException se llame dos veces porque es un atributo personalizado: creo que esto también ocurre con el atributo HandleError estándar, ahora es simplemente visible ya que estoy creando un registro de él.
En última instancia, quiero registrar todas las excepciones no controladas (una vez), conservando las funciones que ofrece [HandleError], en particular establecer diferentes vistas para excepciones de métodos particulares. ¿Hay una manera limpia de hacer esto?
Prueba esto,
public class MyHandleErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
var exceptionHandled = context.ExceptionHandled;
base.OnException(context);
if(!exceptionHandled && context.ExceptionHandled)
// log the error.
}
}
Creo que encontré una solución limpia para esto yo mismo. La extensión de HandleError me pareció una buena idea, pero ahora creo que fue un paso en la dirección equivocada. No quería manejar ningún error de forma diferente, solo escribir excepciones para iniciar sesión antes de que HandleError los recogiera. Debido a esto, HandleError predeterminado puede dejarse en su lugar tal como está. Aunque OnException se puede llamar varias veces, parece ser completamente benigno en la implementación estándar de HandleErrorAttribute.
En cambio, creé un filtro de registro de excepción:
public class LoggedExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
// logging code
}
}
No necesita heredarse también de FilterAttribute
ya que solo se ha registrado una vez en RegisterGlobalFilters junto a HandleErrorAttribute.
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new LoggedExceptionFilter());
filters.Add(new HandleErrorAttribute());
}
Esto permite que las excepciones se [HandleError]
ordenadamente sin cambiar las características estándar [HandleError]
De hecho, encontré la solución para evitar que el método OnException se disparara dos veces. Si está utilizando En el método FilterConfig.RegisterGlobalFilters (), comente el registro de HandleErrorAttribute:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
//filters.Add(new HandleErrorAttribute());
}
}
De hecho, también utilicé el HandleErrorAttribute incorporado sin registrarlo, y funcionó bien. Solo necesito activar los errores personalizados:
<system.web>
<customErrors mode="On" />
</system.web>