MVC [HandleError] HandleErrorAttribute вызывается дважды при использовании глобального журнала
В веб-приложении MVC3 я использовал
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
применить глобальную обработку ошибок, когда пользователю было показано представление "Ошибка", если произошло необработанное исключение.
Для одного конкретного представления я также хотел, чтобы отображалось другое представление ошибки, если возникло необработанное исключение, украсив метод с помощью [HandleError(View = "SpecialError")]
. Это отлично работает.
Затем я хотел добавить глобальный журнал необработанных исключений. Я создал собственный атрибут HandleError с кодом регистрации:
public class MyHandleErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
// Write to log code
base.OnException(context);
}
}
И обновленные RegGlobalFilters и украшение метода, чтобы использовать это имя атрибута вместо. Это работает вообще, но когда исключение возникает в методе, украшенном MyHandleError(View = "SpecialError")]
, метод OnException вызывается дважды. Первоначально предполагалось, что декорирование метода с помощью этого атрибута заменяет глобальный обработчик, но кажется, что оно просто добавляется (что имеет больше смысла, но это не то, что я хочу). Вызывая OnException дважды, одно и то же исключение записывается дважды, что не должно происходить. Я не думаю, что OnException вызывается дважды, потому что это настраиваемый атрибут. Я считаю, что это происходит и со стандартным атрибутом HandleError, но теперь это просто видно, поскольку я создаю его запись.
В конечном счете, я хочу регистрировать все необработанные исключения (один раз), сохраняя функции, предлагаемые [HandleError], в частности, устанавливая разные представления для определенных исключений метода. Есть ли чистый способ сделать это?
Ответы
Ответ 1
Я считаю, что сам нашел для себя чистое решение. Расширение HandleError показалось хорошей идеей, но теперь я думаю, что это был шаг в неправильном направлении. Я не хотел обрабатывать какие-либо ошибки по-разному, просто пишите исключения для журнала один раз, прежде чем HandleError их подбирает. Из-за этого, HandleError по умолчанию может быть оставлен на месте как есть. Хотя OnException можно вызывать несколько раз, он, по-видимому, является полностью мягким в стандартной реализации HandleErrorAttribute.
Вместо этого я создал фильтр регистрации исключений:
public class LoggedExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
// logging code
}
}
Он не нуждается в наследовании от FilterAttribute
, поскольку он только что зарегистрирован один раз в пределах RegisterGlobalFilters вместе с HandleErrorAttribute.
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new LoggedExceptionFilter());
filters.Add(new HandleErrorAttribute());
}
Это позволяет записывать исключения, не меняя стандартные [HandleError]
функции
Ответ 2
Попробуйте это,
public class MyHandleErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
var exceptionHandled = context.ExceptionHandled;
base.OnException(context);
if(!exceptionHandled && context.ExceptionHandled)
// log the error.
}
}
Ответ 3
Вы можете создать пользовательский IFilterProvider
, который будет проверять, был ли фильтр уже применен к этому действию:
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);
}
}
}
Затем вместо регистрации вашего фильтра с помощью GlobalFilterCollection
, вы зарегистрируете поставщика фильтра в Application_Start()
FilterProviders.Providers.Add(new MyFilterProvider());
Альтернативно (аналогично предложению @Mark) вы можете явно установить свойство ExceptionHandled
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;
}
}
Ответ 4
Я действительно нашел решение, чтобы метод OnException дважды срабатывал. Если вы используете метод FilterConfig.RegisterGlobalFilters(), закомментируйте регистрацию HandleErrorAttribute:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
//filters.Add(new HandleErrorAttribute());
}
}
Фактически, я также использовал встроенный HandleErrorAttribute, не регистрируя его, и он работал нормально. Мне нужно только настроить пользовательские ошибки:
<system.web>
<customErrors mode="On" />
</system.web>