如何在Web API 2异步操作中获取正确的异常堆栈跟踪?

时间:2014-01-07 16:44:08

标签: c# asp.net asp.net-web-api

我有一个简单的API控制器方法

public async Task<Models.Timesheet> GetByDate(DateTime date, string user = null)
{
    throw new InvalidOperationException();
}

现在的问题是,我在自定义操作过滤器中或通过设置IncludeErrorDetailPolicy.Always得到的异常堆栈跟踪就像这样

System.InvalidOperationException: Operation is not valid due to the current state of the object.
   at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.ApiController.<InvokeActionWithExceptionFilters>d__1.MoveNext()

Web API v1曾经好多了。在升级到v2之后,堆栈跟踪几乎无法使用 - 预计使用async / await时,堆栈跟踪将不会像过去那样,但在这种情况下,整个堆栈跟踪不包括任何提示,即使是类失败。是否可以在Web API中配置某些内容以缓解此问题?

3 个答案:

答案 0 :(得分:7)

WebApi 2.1(程序集版本5.1.1)

中已修复此问题

按照以下说明升级到最新版本:https://www.nuget.org/packages/Microsoft.AspNet.WebApi

答案 1 :(得分:4)

因此,在我提出问题之后,我有正确的动机来正确挖掘Web API代码以找到问题。似乎罪魁祸首是ActionFilterAttribute.CallOnActionExecutedAsync方法做了这样的事情(释义,而不是实际的代码):

try
{
    await previous();
}
catch(Exception ex)
{
    exception = ex;
}

var context = new HttpActionExecutedContext(actionContext, exception);
this.OnActionExecuted(context);
if (context.Response == null && context.Exception != null)
    throw context.Exception;

所以碰巧第一个过滤器实际上获得了正确的堆栈跟踪。但是它只是去重新抛出异常,从而失去了原始的堆栈跟踪。

这让我意识到,我需要从ExceptionFilterAttribute派生而不是从ActionFilterAttribute派生,因为前者总是被称为最后。不幸的是,我还需要确保我的异常过滤器首先运行,但这不是内置的Web API(see this question以获得正确的解决方案)。

我的快速解决方案是确保我在应用程序中拥有的所有过滤器(我只有几个)派生自我的自定义异常过滤器(并调用base.OnActionExecuted()),因此每个过滤器都会实际检查异常并且执行类似这样的事情:

public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
    if (actionExecutedContext.Exception == null)
        return;

    WriteLog(actionExecutedContext.Exception);

    actionExecutedContext.Response = actionExecutedContext.Request.CreateErrorResponse(
        System.Net.HttpStatusCode.InternalServerError,
        actionExecutedContext.Exception.Message,
        actionExecutedContext.Exception);

    actionExecutedContext.Exception = null;
}

通过这种解决方法,如果启用了错误详细信息,我会在日志和用户中获得正确的堆栈跟踪。

答案 2 :(得分:1)

我相信在Windows 8.1 / Server 2012 R2上运行时,.NET 4.5.1上的堆栈跟踪得到了改进。

或者,我确实有一个"Async Diagnostics" NuGet package可以安装到您的项目中。然后添加以下行:

[assembly: AsyncDiagnosticAspect]

您可以在ToAsyncDiagnosticString类型上使用Exception扩展方法。 ToAsyncDiagnosticString包含来自ToString的所有信息,然后附加“逻辑堆栈”。更多文档(和来源)on GitHub

请注意,不支持部分信任,这在Debug版本中效果最佳。