【问题标题】:How to get correct stack trace for exceptions in Web API 2 async actions?如何为 Web API 2 异步操作中的异常获取正确的堆栈跟踪?
【发布时间】:2014-01-25 11:03:52
【问题描述】:

我有一个简单的 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 中配置一些东西来缓解这个问题?

【问题讨论】:

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


    【解决方案1】:

    这已在 WebApi 2.1(程序集版本 5.1.1)中修复

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

    【讨论】:

    • 这没有提供问题的答案。要批评或要求作者澄清,请在其帖子下方发表评论。
    • 如果这些是规则,那我以后会遵守的。但是,请考虑这一点 - 我对作者没有任何批评,这是 WebApi 5.0.0 中唯一的解决方案,但现在有更好的解决方案 - 升级到 WebApi v5.1.1
    • 这确实是最好的答案,这是一个框架错误,修复它的最佳方法是升级。
    • 没有 WebApi 5.1.1 这样的东西。那是 MVC 的版本号,这里不适用。请修正答案,如果可能,请提供下载链接等,以便我可以将其标记为已接受的答案。
    【解决方案2】:

    因此,在我提出问题后,我得到了正确挖掘 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;
    

    因此,第一个过滤器实际上获得了正确的堆栈跟踪。但随后它会重新抛出异常,从而丢失原始堆栈跟踪。

    这让我意识到,我需要从ActionFilterAttribute 派生而不是从ExceptionFilterAttribute 派生,因为前者总是被最后调用。不幸的是,我还需要确保我的异常过滤器首先运行,但这不是内置的 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;
    }
    

    通过这个解决方法,如果打开了错误详细信息,我可以得到正确的堆栈跟踪到日志和用户。

    【讨论】:

    • 请将此作为错误打开,以便 WebAPI 团队意识到问题。
    • 仅供参考...发布 Web API 2,此问题已得到修复,您将按预期看到堆栈跟踪。
    【解决方案3】:

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

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

    [assembly: AsyncDiagnosticAspect]
    

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-22
      • 2011-06-01
      • 1970-01-01
      • 2011-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多