124

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

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

  • 创建并注册一个ExceptionHandlingAttribute
  • 实现一个Application_Error方法Global.asax.cs
  • 订阅AppDomain.CurrentDomain.UnhandledException
  • 订阅TaskScheduler.UnobservedTaskException

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

  • IQueryable动作方法返回的方法执行失败时抛出的异常
  • 消息处理程序抛出的异常(即HttpConfiguration.MessageHandlers
  • 创建控制器实例时抛出的异常

基本上,如果异常会导致 500 Internal Server Error 返回给客户端,我希望它被记录下来。在 Web 表单和 MVC 中实施Application_Error这项工作做得很好 - 我可以在 Web Api 中使用什么?

4

5 回答 5

163

这现在可以通过 WebAPI 2.1 实现(请参阅新增功能):

创建 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());
于 2014-01-27T13:53:37.280 回答
21

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

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

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

如果您同意,请转到该链接并投票!

与此同时,优秀的文章ASP.NET Web API 异常处理展示了几种不同的方法来捕获不同类别的错误。它比应有的复杂得多,并且不能捕获所有内部服务器错误,但它是当今可用的最佳方法。

更新:全局错误处理现已实现并可在夜间构建中使用!它将在 ASP.NET MVC v5.1 中发布。以下是它的工作原理:https ://aspnetwebstack.codeplex.com/wikipage?title=Global%20Error%20Handling

于 2013-04-22T23:59:38.630 回答
21

Yuval 的答案是定制对 Web API 捕获的未处理异常的响应,而不是用于记录,如链接页面上所述。有关详细信息,请参阅页面上的“何时使用”部分。记录器总是被调用,但处理程序只有在可以发送响应时才会被调用。简而言之,使用logger进行记录,使用 handler 自定义响应。

顺便说一句,我正在使用程序集 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);
    }
}

请注意,与记录器不同的是,您通过替换默认处理程序而不是添加来注册处理程序。

config.Services.Replace(typeof(IExceptionHandler),
    new OopsExceptionHandler(config.Services.GetExceptionHandler()));
于 2016-03-23T05:20:30.577 回答
11

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

IExceptionHandler 处理来自所有控制器的所有未处理的异常。这是列表中的最后一个。如果发生异常,将首先调用 IExceptionLogger,然后调用控制器 ExceptionFilters,如果仍未处理,则调用 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);
        }
    }
}

更多关于这里

于 2015-07-12T11:40:02.780 回答
-1

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

我认为我的新global.asax.Application_Error方法并没有被我们的遗留代码中的未处理异常持续调用。

然后我在调用堆栈中间发现了一些 try-catch 块,它们在异常文本上调用了 Response.Write。就是这样。转储屏幕上的文字然后杀死异常石死。

所以异常被处理了,但处理没有做任何有用的事情。一旦我删除了这些 try-catch 块,异常就会按预期传播到 Application_Error 方法。

于 2017-10-10T13:40:24.757 回答