在 ASP.NET Web Api 中捕获所有未处理的异常
Posted
技术标签:
【中文标题】在 ASP.NET Web Api 中捕获所有未处理的异常【英文标题】:catch all unhandled exceptions in ASP.NET Web Api 【发布时间】:2013-04-08 09:04:55 【问题描述】:如何捕获 所有 发生在 ASP.NET Web Api 中的未处理异常以便记录它们?
到目前为止我已经尝试过:
创建并注册ExceptionHandlingAttribute
在Global.asax.cs
中实现Application_Error
方法
订阅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 管道异常。 谢谢 - 我花了一段时间才弄清楚为什么我无法记录我的构造函数/依赖注入问题,我以为我已经对 WebAPI 日志进行了排序...... 【参考方案1】:这现在可以通过 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());
【讨论】:
@NeilBarnwell 是的,Web API 2.1 对应 System.Web.Http 程序集版本 5.1.0。因此,您需要此版本或更高版本才能使用此处描述的解决方案。见the nuget package versions 某些 500 错误仍然不会被此捕获,例如。 HttpException - 远程主机关闭了连接。 global.asax Application_Error 是否还有地方可以处理 web api 处理之外的错误? 我喜欢官方文档在 msdn 上的详细程度,99% 的开发人员真正想要的只是记录错误的 8 行代码。 我确信它仍然无法捕获所有 500 错误:例如,如果在自定义 Owin 中间件的 Invoke 方法中抛出错误!【参考方案2】:要回答我自己的问题,这是不可能的!
处理所有导致内部服务器错误的异常似乎是 Web API 应该具备的基本能力,因此我向 Microsoft 提出了Web API 的全局错误处理程序的请求:
https://aspnetwebstack.codeplex.com/workitem/1001
如果您同意,请转到该链接并投票!
同时,优秀的文章ASP.NET Web API Exception Handling 展示了几种不同的方法来捕获不同类别的错误。它比应有的复杂得多,而且它不会捕获所有内部服务器错误,但它是当今可用的最佳方法。
更新:全局错误处理现已实现并可在夜间构建中使用!它将在 ASP.NET MVC v5.1 中发布。以下是它的工作原理:https://aspnetwebstack.codeplex.com/wikipage?title=Global%20Error%20Handling
【讨论】:
似乎是使用控制器进行 ajax 调用而不是 web api 的理由。边界已经很模糊了。虽然如果 ELMAH 能够捕获它,也许有办法 Web API 2.1 中现在添加了全局错误处理。有关详细信息,请参阅我的答案。【参考方案3】:Yuval 的答案是自定义对 Web API 捕获的未处理异常的响应,而不是用于记录,如链接的 page 中所述。有关详细信息,请参阅页面上的“何时使用”部分。始终调用记录器,但仅在可以发送响应时才调用处理程序。简而言之,使用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()));
【讨论】:
这是一个很好的解决方案,这应该是“捕获或记录所有错误”的公认解决方案。当我只是扩展 ExceptionHandler 时,我永远无法弄清楚为什么它对我不起作用。 很好的解决方案。一旦为请求加载了 MVC 管道,这将非常有效。在此之前,IIS 仍会处理异常,包括在 startup.cs 中启动 OWIN 时。但是,在 spin up 完成处理 startup.cs 后的某个时刻,它的工作非常出色。【参考方案4】:您还可以通过实现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);
更多关于 here.
【讨论】:
只是好奇,这是在哪里注册的? Handler 可以注册到你的依赖注入框架中【参考方案5】:您可能有自己不知道的现有 try-catch 块。
我认为我的新 global.asax.Application_Error
方法在我们的旧代码中没有被一致地调用以处理未处理的异常。
然后我在调用堆栈的中间发现了一些 try-catch 块,它们在异常文本上调用了 Response.Write。就是这样。将文本转储到屏幕上然后杀死异常石死。
所以异常被处理了,但是处理没有做任何有用的事情。一旦我删除了这些 try-catch 块,异常就会按预期传播到 Application_Error 方法。
【讨论】:
以上是关于在 ASP.NET Web Api 中捕获所有未处理的异常的主要内容,如果未能解决你的问题,请参考以下文章