2012-07-12 44 views
10

当在一个MVC3 web应用我用使用全局日志

public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
{ 
    filters.Add(new HandleErrorAttribute()); 
} 

应用全局错误处理,其中用户被示出的“错误”视图如果未处理的异常出现调用两次MVC [的HandleError] 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属性,它现在简单地可见,因为我正在创建它的记录。最终,我想记录所有未处理的异常(一次),同时保留[HandleError]提供的功能,特别是为特定的方法异常设置不同的视图。有没有一种干净的方式来做到这一点?

回答

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

谢谢!这对我也很有用。 – abjbhat 2013-08-01 06:40:53

+0

嗨,很好的解决方案,但你怎么能从异常的来源,如类/方法的名称获得更多的信息? – Patrick 2014-04-17 11:01:38

2

你可以创建一个自定义IFilterProvider将检查过滤器已经被应用到行动:而不是与GlobalFilterCollection注册您的过滤器

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

然后,你会登记在Application_Start()您的过滤器供应商

FilterProviders.Providers.Add(new MyFilterProvider()); 

或者(类似于@马克提出的),你可以明确地设置的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; 
    } 
} 
3

0财产试试这个,

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

     base.OnException(context);       

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

这是一个完美的技巧。谢谢你对我很好。谢谢 – mayk 2016-05-13 11:11:21

-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> 
+0

也许你应该重新阅读这个问题。他想同时使用两者。 – 2013-05-23 23:54:22