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

Joe*_*ley 112 c# unhandled-exception asp.net-web-api

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

到目前为止,我尝试过:

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

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

  • IQueryableaction方法返回的异常无法执行时抛出的异常
  • 消息处理程序抛出的异常(即HttpConfiguration.MessageHandlers)
  • 创建控制器实例时抛出异常

基本上,如果异常将导致500内部服务器错误返回到客户端,我希望它被记录.实现Application_Error在Web窗体和MVC中完成了这项工作 - 我可以在Web Api中使用什么?

dec*_*tes 155

现在可以使用WebAPI 2.1(参见新功能):

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

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

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

config.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());
Run Code Online (Sandbox Code Playgroud)

或直接:

GlobalConfiguration.Configuration.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢官方doco在msdn上的详细程度,99%的开发人员真正想要的只是记录错误的8行代码. (11认同)
  • @NeilBarnwell是的,Web API 2.1对应于System.Web.Http程序集版本**5.1.0**.因此,您需要此版本或更高版本才能使用此处描述的解决方案.参见[nuget包版本](https://www.nuget.org/packages/Microsoft.AspNet.WebApi/5.1.0) (7认同)
  • 这似乎在WebAPI 5.0.0中不起作用. (2认同)
  • 某些500错误仍未被此捕获,例如.HttpException - 远程主机关闭连接.是否仍有一个地方可以使用global.asax Application_Error来处理web api处理之外的错误? (2认同)

Duo*_*ran 20

Yuval的答案是自定义对Web API捕获的未处理异常的响应,而不是用于记录,如链接页面上所述.有关详细信息,请参阅页面上的"何时使用"部分.始终调用记录器,但只有在可以发送响应时才调用处理程序.简而言之,使用记录器记录和处理程序来自定义响应.

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

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

config.Services.Replace(typeof(IExceptionHandler),
    new OopsExceptionHandler(config.Services.GetExceptionHandler()));
Run Code Online (Sandbox Code Playgroud)


Joe*_*ley 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 2.1中添加了全局错误处理.有关详细信息,请参阅我的回答 (4认同)

Yuv*_*kov 9

您还可以通过实现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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

更多关于这一点.

  • 很好奇,这个是在哪里注册的? (2认同)