catch all unhandled exceptions in ASP.NET Web Api

如何捕获 ASP.NET Web Api 中发生的 所有未处理异常,以便记录它们?

到目前为止,我已经试过了:

  • Create and register an ExceptionHandlingAttribute
  • Global.asax.cs中实现 Application_Error方法
  • 订阅 AppDomain.CurrentDomain.UnhandledException
  • 订阅 TaskScheduler.UnobservedTaskException

ExceptionHandlingAttribute成功地处理了在控制器操作方法和操作过滤器中抛出的异常,但是没有处理其他异常,例如:

  • 当操作方法返回的 IQueryable无法执行时引发的异常
  • 消息处理程序(即 HttpConfiguration.MessageHandlers)引发的异常
  • 创建控制器实例时引发的异常

基本上,如果一个异常将导致一个500内部服务器错误返回给客户端,我希望它被记录。实现 Application_Error在 Web 窗体和 MVC 中做得很好——我可以在 Web API 中使用什么?

65277 次浏览

回答我自己的问题,这是不可能的!

处理所有导致内部服务器错误的异常似乎是 Web API 应该具备的基本功能,因此我向 Microsoft 提交了一个 Web API 的全局错误处理程序请求:

Https://aspnetwebstack.codeplex.com/workitem/1001

If you agree, go to that link and vote for it!

与此同时,优秀的文章 Web API 异常处理展示了一些不同的方法来捕捉一些不同类别的错误。它比应该的要复杂得多,而且它不能捕获 所有内部服务器错误,但它是目前可用的最佳方法。

更新: 全局错误处理现在已经实现,并且可以在夜间构建中使用!它将在 ASP.NET MVC v5.1中发布。它是这样工作的: < a href = “ https://aspnetwebstack.co落 x.com/wikipage? title = Global% 20Error% 20Processing”> https://aspnetwebstack.codeplex.com/wikipage?title=global%20error%20handling

WebAPI 2.1现在可以做到这一点(参见 What's New) :

创建一个或多个 IExceptionLogger 实现,例如:

public class TraceExceptionLogger : ExceptionLogger
{
public override void Log(ExceptionLoggerContext context)
{
Trace.TraceError(context.ExceptionContext.Exception.ToString());
}
}

然后在配置回调中注册应用程序的 HttpConfiguration,如下所示:

config.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());

或直接:

GlobalConfiguration.Configuration.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());

还可以通过实现 IExceptionHandler接口(或继承 ExceptionHandler基类)创建全局异常处理程序。它将是执行链中最后一个被调用的,毕竟已经注册了 IExceptionLogger:

IExceptionHandler 处理来自所有 这是列表中的最后一个。如果发生异常,则 将首先调用 IExceptionLogger,然后调用控制器 ExceptionFilters and if still unhandled, the IExceptionHandler 实施。

public class OopsExceptionHandler : ExceptionHandler
{
public override void HandleCore(ExceptionHandlerContext context)
{
context.Result = new TextPlainErrorResult
{
Request = context.ExceptionContext.Request,
Content = "Oops! Sorry! Something went wrong."
};
}


private class TextPlainErrorResult : IHttpActionResult
{
public HttpRequestMessage Request { get; set; }


public string Content { get; set; }


public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response =
new HttpResponseMessage(HttpStatusCode.InternalServerError);
response.Content = new StringContent(Content);
response.RequestMessage = Request;
return Task.FromResult(response);
}
}
}

More on that 给你.

The Yuval's answer is for customizing responses to unhandled exceptions caught by Web API, not for logging, as noted on the linked 呼叫. Refer to the When to Use section on the page for details. The logger is always called but the handler is called only when a response can be sent. In short, use the 伐木工 to log and the handler to customize the response.

顺便说一下,我使用的是程序集 v5.2.3,而 ExceptionHandler类没有 HandleCore方法。我认为相当于 Handle。然而,简单地子类化 ExceptionHandler(如 Yuval 的回答)是不起作用的。在我的例子中,我必须按照以下方式实现 IExceptionHandler

internal class OopsExceptionHandler : IExceptionHandler
{
private readonly IExceptionHandler _innerHandler;


public OopsExceptionHandler (IExceptionHandler innerHandler)
{
if (innerHandler == null)
throw new ArgumentNullException(nameof(innerHandler));


_innerHandler = innerHandler;
}


public IExceptionHandler InnerHandler
{
get { return _innerHandler; }
}


public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
Handle(context);


return Task.FromResult<object>(null);
}


public void Handle(ExceptionHandlerContext context)
{
// Create your own custom result here...
// In dev, you might want to null out the result
// to display the YSOD.
// context.Result = null;
context.Result = new InternalServerErrorResult(context.Request);
}
}

Note that, unlike the logger, you register your handler by replacing the default handler, not adding.

config.Services.Replace(typeof(IExceptionHandler),
new OopsExceptionHandler(config.Services.GetExceptionHandler()));

您可能有不知道的现有 try-catch 块。

我认为我的新 global.asax.Application_Error方法在遗留代码中没有一直被调用来处理未处理的异常。

然后,我在调用堆栈的中间发现了一些 try-catch 块,这些块称为 Response。写在异常文本上。仅此而已。转储文字在屏幕上,然后杀死异常石死。

所以异常被处理了,但是处理没有做任何有用的事情。一旦我移除了这些 try-catch 块,异常就会如预期的那样传播到 Application _ Error 方法。