10

在我使用的 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 和方法装饰以改用此属性名称。这通常有效,但是当使用 OnException 方法修饰的方法中发生异常时,MyHandleError(View = "SpecialError")]会调用两次。我最初假设用这个属性装饰方法取代了全局处理程序,但似乎它只是简单地添加到(这更有意义,但这不是我想要的)。通过调用 OnException 两次,相同的异常会被记录两次,这是绝对不能发生的。我不认为 OnException 被调用了两次,因为它是一个自定义属性——我相信标准的 HandleError 属性也会发生这种情况,它现在在我创建它的记录时是可见的。

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

4

4 回答 4

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]这允许在不更改标准功能的情况下整齐地记录异常

于 2012-07-14T00:24:39.460 回答
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.
    }
}
于 2012-07-12T11:41:38.943 回答
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);
        }
    }
}

然后,您无需在 中注册您的过滤器,而是GlobalFilterCollectionApplication_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;
    }
}
于 2012-07-12T11:41:29.223 回答
-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>
于 2013-05-23T23:35:08.780 回答