2013-03-01 85 views
158

背景

我正在为客户端开发API服务层,并且我被要求捕获并记录全局错误。如何为C#MVC4 WebAPI应用程序全局记录所有异常?

因此,尽管像一个未知的终结点(或行为)很容易通过使用ELMAH或通过添加像这样的Global.asax处理:

protected void Application_Error() 
{ 
    Exception unhandledException = Server.GetLastError(); 
    //do more stuff 
} 

。 。与路由无关的未处理的错误不会被记录。例如:

public class ReportController : ApiController 
{ 
    public int test() 
    { 
     var foo = Convert.ToInt32("a");//Will throw error but isn't logged!! 
     return foo; 
    } 
} 

我自己也尝试通过注册该过滤器全局设置[HandleError]属性:

filters.Add(new HandleErrorAttribute()); 

但也不会记录所有的错误。

问题/疑问

如何截取喜欢通过调用/test上面,这样我就可以登录他们产生一个错误?看起来这个答案应该是显而易见的,但我已经尝试了迄今为止我能想到的一切。

理想情况下,我想添加一些东西到错误日志记录中,例如请求用户的IP地址,日期,时间等等。我也希望能够在遇到错误时自动发送电子邮件给支持人员。所有这些我都可以做,只要我能在发生这些错误时拦截这些错误!

已解决!

感谢Darin Dimitrov,我接受了他的回答,我弄明白了。 WebAPI不会不是以与常规MVC控制器相同的方式处理错误。

这里是什么工作:

1)添加自定义过滤器,您的命名空间:

public class ExceptionHandlingAttribute : ExceptionFilterAttribute 
{ 
    public override void OnException(HttpActionExecutedContext context) 
    { 
     if (context.Exception is BusinessException) 
     { 
      throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError) 
      { 
       Content = new StringContent(context.Exception.Message), 
       ReasonPhrase = "Exception" 
      }); 

     } 

     //Log Critical errors 
     Debug.WriteLine(context.Exception); 

     throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError) 
     { 
      Content = new StringContent("An error occurred, please try again or contact the administrator."), 
      ReasonPhrase = "Critical Exception" 
     }); 
    } 
} 

2)现在全球注册过滤器在WebApiConfig类:

public static class WebApiConfig 
{ 
    public static void Register(HttpConfiguration config) 
    { 
     config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{action}/{id}", new { id = RouteParameter.Optional }); 
     config.Filters.Add(new ExceptionHandlingAttribute()); 
    } 
} 

你可以跳过注册,只是装饰一个单控制器与[ExceptionHandling]属性。

+0

我有同样的问题。未处理的异常会被捕获到异常过滤器属性中,但是当我抛出一个新的异常时,它不会被异常过滤器属性捕获,对此有什么想法? – daveBM 2013-11-05 11:54:02

+1

未知的API控制器调用像http:// myhost/api/undefinedapicontroller错误仍未捕获。 Application_error和Exception过滤器代码不会被执行。如何抓住他们呢? – Andrus 2013-11-26 09:03:37

+1

全局错误处理已添加到WebAPI v2.1。请参阅我的回复:http:// stackoverflow。com/questions/17449400/how-do-i-set-up -a-global-error-handler-in-webapi/21264726#21264726 – DarrellNorton 2014-01-21 17:13:35

回答

53

如果您的Web API托管在ASP.NET应用程序中,将调用代码中所有未处理的异常(包括您在测试操作中显示的异常)中的 Application_Error事件。所以你所要做的就是在Application_Error事件中处理这个异常。在示例代码中,您显示的只是处理 HttpException类型的异常,而 Convert.ToInt32("a")代码显然不是这种情况。因此,请确保您登录和处理所有异常在那里:在Web API中

protected void Application_Error() 
{ 
    Exception unhandledException = Server.GetLastError(); 
    HttpException httpException = unhandledException as HttpException; 
    if (httpException == null) 
    { 
     Exception innerException = unhandledException.InnerException; 
     httpException = innerException as HttpException; 
    } 

    if (httpException != null) 
    { 
     int httpCode = httpException.GetHttpCode(); 
     switch (httpCode) 
     { 
      case (int)HttpStatusCode.Unauthorized: 
       Response.Redirect("/Http/Error401"); 
       break; 

      // TODO: don't forget that here you have many other status codes to test 
      // and handle in addition to 401. 
     } 
     else 
     { 
      // It was not an HttpException. This will be executed for your test action. 
      // Here you should log and handle this case. Use the unhandledException instance here 
     } 
    } 
} 

异常处理可在各级进行。这里有一个detailed article解释不同的可能性:这可以被注册为一个全球性的异常过滤器

  • 自定义异常筛选器属性

    [AttributeUsage(AttributeTargets.All)] 
    public class ExceptionHandlingAttribute : ExceptionFilterAttribute 
    { 
        public override void OnException(HttpActionExecutedContext context) 
        { 
         if (context.Exception is BusinessException) 
         { 
          throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError) 
          { 
           Content = new StringContent(context.Exception.Message), 
           ReasonPhrase = "Exception" 
          }); 
         } 
    
         //Log Critical errors 
         Debug.WriteLine(context.Exception); 
    
         throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError) 
         { 
          Content = new StringContent("An error occurred, please try again or contact the administrator."), 
          ReasonPhrase = "Critical Exception" 
         }); 
        } 
    } 
    
  • 自定义操作调用

    public class MyApiControllerActionInvoker : ApiControllerActionInvoker 
    { 
        public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken) 
        { 
         var result = base.InvokeActionAsync(actionContext, cancellationToken); 
    
         if (result.Exception != null && result.Exception.GetBaseException() != null) 
         { 
          var baseException = result.Exception.GetBaseException(); 
    
          if (baseException is BusinessException) 
          { 
           return Task.Run<HttpResponseMessage>(() => new HttpResponseMessage(HttpStatusCode.InternalServerError) 
           { 
            Content = new StringContent(baseException.Message), 
            ReasonPhrase = "Error" 
    
           }); 
          } 
          else 
          { 
           //Log critical error 
           Debug.WriteLine(baseException); 
    
           return Task.Run<HttpResponseMessage>(() => new HttpResponseMessage(HttpStatusCode.InternalServerError) 
           { 
            Content = new StringContent(baseException.Message), 
            ReasonPhrase = "Critical Error" 
           }); 
          } 
         } 
    
         return result; 
        } 
    } 
    
+1

简单又干净 – tchrikch 2013-03-01 22:48:49

+0

我希望它那么简单,但错误仍然没有被抓住。我已经更新了这个问题以避免混淆。谢谢。 – 2013-03-01 22:49:30

+0

@MatthewPatrickCashatt,如果这个异常没有被'Application_Error'事件捕获,这意味着其他代码在之前正在使用它。例如,你可能有一些自定义的HandleErrorAttributes,自定义模块......有其他地方的gazillions可以捕获和处理异常。但是最好的地方是Application_Error事件,因为那是所有未处理的异常都要结束的地方。 – 2013-03-01 22:51:22

2

将整个事情包装在try/catch中并记录未处理的异常,然后传递它。除非有更好的内置方式来做到这一点。

这里有一个参考Catch All (handled or unhandled) Exceptions

(编辑:哦API)

+0

为了以防万一,他还需要重新抛出异常。 – DigCamara 2013-03-01 22:48:23

+0

@DigCamara对不起,这就是我的意思传递它。扔;应该处理的。我原本说“决定是退出还是重新加载”,然后意识到他说过这是一个API。在这种情况下,最好让App通过传递来决定它想做什么。 – Tim 2013-03-01 22:57:25

+0

这是一个不好的答案,因为它会导致在每个操作中加载重复的代码。 – Jansky 2017-11-27 14:17:43

2

你有没有想过做这样的处理错误行为过滤像

[HandleError] 
public class BaseController : Controller {...} 

,你还可以创建一个自定义版本[HandleError]与您可以写错误信息和所有其他详细信息登录

+0

谢谢,但我已经在全球设置了。它提出了和上面相同的问题,并不是所有的错误都被记录下来。 – 2013-03-01 22:42:51

7

为什么重新抛出等?这个工作,它会使服务返回状态500等

public class LogExceptionFilter : ExceptionFilterAttribute 
{ 
    private static readonly ILog log = LogManager.GetLogger(typeof (LogExceptionFilter)); 

    public override void OnException(HttpActionExecutedContext actionExecutedContext) 
    { 
     log.Error("Unhandeled Exception", actionExecutedContext.Exception); 
     base.OnException(actionExecutedContext); 
    } 
} 
75

作为以前答案的补充。

昨天,ASP.NET Web API 2.1被破解为released
它为全球处理异常提供了另一个机会。
详细信息请参见sample

简而言之,您将添加全局异常记录器和/或全局异常处理程序(仅一个)。
将它们添加到配置:

public static void Register(HttpConfiguration config) 
{ 
    config.MapHttpAttributeRoutes(); 

    // There can be multiple exception loggers. 
    // (By default, no exception loggers are registered.) 
    config.Services.Add(typeof(IExceptionLogger), new ElmahExceptionLogger()); 

    // There must be exactly one exception handler. 
    // (There is a default one that may be replaced.) 
    config.Services.Replace(typeof(IExceptionHandler), new GenericTextExceptionHandler()); 
} 

及其实现途径:

public class ElmahExceptionLogger : ExceptionLogger 
{ 
    public override void Log(ExceptionLoggerContext context) 
    { 
    ... 
    } 
} 

public class GenericTextExceptionHandler : ExceptionHandler 
{ 
    public override void Handle(ExceptionHandlerContext context) 
    { 
    context.Result = new InternalServerErrorTextPlainResult(
     "An unhandled exception occurred; check the log for more information.", 
     Encoding.UTF8, 
     context.Request); 
    } 
} 
+1

这个工作完美无缺。我记录和并发处理(因为我得到了logID并将其传回,以便用户可以添加注释),所以我将Result设置为一个新的ResponseMessageResult。这一直困扰着我一段时间,谢谢。 – Brett 2014-09-05 23:41:57

+0

这确实有帮助。谢谢! – thebiggestlebowski 2014-12-17 05:38:07

相关问题