捕获ASP.NET Web Api中所有未处理的异常


113

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

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

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

ExceptionHandlingAttribute成功处理时引发的控制器操作方法和操作筛选范围内,但其他异常没有被处理,例如例外:

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

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


您是否尝试过使用ASP.NET Health Monitoring?只需启用它,看看您的异常是否未记录到事件日志中。
约翰·桑德斯

运行状况监视捕获我的MVC管道异常,但不捕获我的Web Api管道异常。
Joe Daley

谢谢-花了我一段时间才弄清楚为什么我无法记录构造函数/依赖项注入问题,我以为我已经对WebAPI记录进行了排序...
2013年

Answers:


156

现在,可以使用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());

7
@NeilBarnwell是的,Web API 2.1对应于System.Web.Http程序集版本5.1.0。因此,您需要此版本或更高版本才能使用此处描述的解决方案。查看nuget软件包的版本
2014年

2
例如,某些500个错误仍然无法解决。HttpException-远程主机关闭了连接。仍然有global.asax Application_Error处理Web api处理之外的错误的地方吗?
阿夫纳

11
我喜欢官方的doco在msdn上的详细程度,而99%的开发人员真正想要的只是8行代码来记录错误。
罗克兰

20

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

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

1
这是一个很棒的解决方案,应该是“捕获或记录所有错误”的公认解决方案。当我扩展ExceptionHandler时,我永远无法弄清楚为什么它对我不起作用。
拉吉夫

很好的解决方案。一旦为请求加载了MVC管道,这将非常有用。直到那时,IIS仍会处理异常,包括在startup.cs中启动OWIN时。但是,在启动完成处理startup.cs之后的某个时刻,它的工作非常出色。
Gustyn

18

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

处理所有导致内部服务器错误的异常似乎是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


似乎是使用控制器而不是Web api进行ajax调用的原因。边界已经模糊了。尽管ELMAH能够捕获它,也许有一种方法
Sonic Soul

4
现在,Web API 2.1中添加了全局错误处理。请参阅我的答案以获取更多详细信息。
2014年

10

您还可以通过实现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);
        }
    }
}

这里更多。


只是好奇,这在哪里注册?
ThunD3eR

-1

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

我以为我的新global.asax.Application_Error方法并未在我们的旧代码中始终被要求处理未处理的异常。

然后,我在调用堆栈的中间找到了一些try-catch块,它们在Exception文本上调用了Response.Write。就是这样 将文本倾倒在屏幕上,然后杀死异常死亡的石头。

因此正在处理异常,但是处理没有任何用处。一旦删除了这些try-catch块,异常就会按预期传播到Application_Error方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.