使用全局日志记录时,MVC [HandleError] HandleErrorAttribute被调用两次

时间:2012-07-12 10:27:56

标签: asp.net asp.net-mvc asp.net-mvc-3 error-handling

在我使用的MVC3 Web应用程序中

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);
        }
    }

并更新RegisterGlobalFilters和方法修饰以改为使用此属性名称。这通常有效但在使用MyHandleError(View = "SpecialError")]修饰的方法中发生异常时,OnException方法被称为两次。我原先假设用这个属性装饰方法取代了全局处理程序,但它似乎只是添加到了(这更有意义,但它不是我想要的)。通过两次调用OnException,将记录两次相同的异常,这不应该发生。我认为OnException不会被调用两次,因为它是一个自定义属性 - 我相信这也会发生在标准的HandleError属性上,现在它只是在我创建它的记录时可见。 / p>

最终,我想记录所有未处理的异常(一次),同时保留[HandleError]提供的功能,特别是为特定方法异常设置不同的视图。这样做有干净的方法吗?

4 个答案:

答案 0 :(得分:9)

我相信我自己找到了一个干净的解决方案。扩展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]功能

的情况下巧妙记录异常

答案 1 :(得分:3)

试试这个,

public class MyHandleErrorAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext context)
    {
         var exceptionHandled = context.ExceptionHandled;

         base.OnException(context);                          

         if(!exceptionHandled && context.ExceptionHandled)
           // log the error.
    }
}

答案 2 :(得分:2)

您可以创建一个自定义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的{​​{1}}属性

ExceptionContext

答案 3 :(得分:-1)

我实际上找到了防止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>