浏览器取消请求时的ASP.NET Web API OperationCanceledException

Bat*_*and 114 asp.net iis task-parallel-library async-await asp.net-web-api

当用户加载页面时,它会发出一个或多个ajax请求,这些请求会命中ASP.NET Web API 2控制器.如果用户导航到另一个页面,则在这些ajax请求完成之前,浏览器会取消这些请求.然后,我们的ELMAH HttpModule为每个取消的请求记录两个错误:

错误1:

System.Threading.Tasks.TaskCanceledException: A task was canceled.
   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.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   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.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   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.ExceptionFilterResult.<ExecuteAsync>d__0.MoveNext()
Run Code Online (Sandbox Code Playgroud)

错误2:

System.OperationCanceledException: The operation was canceled.
   at System.Threading.CancellationToken.ThrowIfCancellationRequested()
   at System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__1b.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.WebHost.HttpControllerHandler.<CopyResponseAsync>d__7.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.WebHost.HttpControllerHandler.<ProcessRequestAsyncCore>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.TaskAsyncHelper.EndTask(IAsyncResult ar)
   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
Run Code Online (Sandbox Code Playgroud)

看看堆栈跟踪,我看到异常是从这里抛出的:https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Http.WebHost/HttpControllerHandler.cs# L413

我的问题是:我如何处理和忽略这些例外?

它似乎不在用户代码中......

笔记:

  • 我正在使用ASP.NET Web API 2
  • Web API端点是异步和非异步方法的混合.
  • 无论我在哪里添加错误日志记录,我都无法在用户代码中捕获异常

dma*_*son 77

这是ASP.NET Web API 2中的一个错误,不幸的是,我认为没有一种解决方法会永远成功.我们提交了一个错误来修复它.

最终,问题是我们在这种情况下将取消的任务返回给ASP.NET,并且ASP.NET将取消的任务视为未处理的异常(它在应用程序事件日志中记录问题).

在此期间,您可以尝试类似下面的代码.它添加了一个顶级消息处理程序,可在取消令牌触发时删除内容.如果响应没有内容,则不应触发该错误.它可能发生的可能性很小,因为客户端可能会在消息处理程序检查取消令牌之后但在更高级别的Web API代码执行相同检查之前断开连接.但我认为在大多数情况下它会有所帮助.

大卫

config.MessageHandlers.Add(new CancelledTaskBugWorkaroundMessageHandler());

class CancelledTaskBugWorkaroundMessageHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

        // Try to suppress response content when the cancellation token has fired; ASP.NET will log to the Application event log if there's content in this case.
        if (cancellationToken.IsCancellationRequested)
        {
            return new HttpResponseMessage(HttpStatusCode.InternalServerError);
        }

        return response;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 当我尝试上面的建议时,我甚至在将请求传递给"SendAsync"之前仍然收到了异常(你可以通过在浏览器中按住`F5`来对你的Api发出请求来模拟这个.我通过在调用"SendAsync"之上添加"if(cancellationToken.IsCancellationRequested)"检查来解决这个问题.现在,当浏览器快速取消请求时,异常不再显示. (3认同)
  • 我发现了更多有关何时发生异常的详细信息,并发现此解决方法仅适用于其中之一。以下是一些详细信息:/sf/answers/3606022311/ (3认同)
  • 作为更新,这确实捕获了一些请求.我们的日志中仍然会看到很多.谢谢你的解决方案.期待修复. (2认同)
  • @KiranChalla - 我可以确认升级到5.2.2仍然有这些错误. (2认同)
  • 作为临时修复,为了阻止业务恐慌,我可以将以下代码添加到我的 ElmahExceptionFilter 中吗?``public override void OnException(HttpActionExecutedContext context) { if (null != context.Exception &amp;&amp; !(context.Exception is TaskCanceledException || context.Exception is OperationCanceledException)) { Elmah.ErrorSignal.FromCurrentContext().Raise(context.Exception) ); } base.OnException(上下文); }`` (2认同)
  • 我仍然得到错误.我已经使用了上述建议,任何其他线索. (2认同)
  • 对于那些想要触发此错误以查看其行为方式的人,最好的方法如下。调试时单击*暂停*。向您的 API 发出请求,然后中止它。*恢复*您的 Web API 应用程序。您将收到OperationCanceledException。 (2认同)

Sha*_*ine 16

为WebApi实现异常记录器时,建议扩展System.Web.Http.ExceptionHandling.ExceptionLogger类而不是创建ExceptionFilter.对于已取消的请求,WebApi内部不会调用ExceptionLoggers的Log方法(但是,异常过滤器将获取它们).这是设计的.

HttpConfiguration.Services.Add(typeof(IExceptionLogger), myWebApiExceptionLogger); 
Run Code Online (Sandbox Code Playgroud)


huy*_*itw 12

以下是此问题的其他解决方法.只需在OWIN管道的开头添加一个自定义OWIN中间件即可捕获OperationCanceledException:

#if !DEBUG
app.Use(async (ctx, next) =>
{
    try
    {
        await next();
    }
    catch (OperationCanceledException)
    {
    }
});
#endif
Run Code Online (Sandbox Code Playgroud)

  • 我主要是从OWIN上下文中得到此错误,而此错误的针对性更好 (2认同)

Ily*_*dik 6

我发现了有关此错误的更多详细信息。有两种可能的异常可能发生:

  1. OperationCanceledException
  2. TaskCanceledException

如果在控制器中的代码执行时连接断开(或者可能还有一些系统代码),则会发生第一个。如果在执行在属性内时断开连接,则会发生第二个(例如AuthorizeAttribute)。

因此,提供的解决方法有助于部分缓解第一个异常,但对第二个异常没有任何帮助。在后一种情况下,TaskCanceledException发生在base.SendAsync调用本身而不是取消标记被设置为 true。

我可以看到解决这些问题的两种方法:

  1. 忽略 global.asax 中的两个异常。那么问题来了,是否有可能突然忽略一些重要的东西?
  2. 在处理程序中进行额外的 try/catch(虽然它不是防弹的 + 仍然有可能TaskCanceledException我们忽略的将是我们想要记录的。

config.MessageHandlers.Add(new CancelledTaskBugWorkaroundMessageHandler());

class CancelledTaskBugWorkaroundMessageHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        try
        {
            HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

            // Try to suppress response content when the cancellation token has fired; ASP.NET will log to the Application event log if there's content in this case.
            if (cancellationToken.IsCancellationRequested)
            {
                return new HttpResponseMessage(HttpStatusCode.InternalServerError);
            }
        }
        catch (TaskCancellationException)
        {
            // Ignore
        }

        return response;
    }
}
Run Code Online (Sandbox Code Playgroud)

我发现我们可以尝试查明错误异常的唯一方法是检查堆栈跟踪是否包含一些 Asp.Net 内容。虽然看起来不是很健壮。

PS这是我过滤这些错误的方式:

private static bool IsAspNetBugException(Exception exception)
{
    return
        (exception is TaskCanceledException || exception is OperationCanceledException) 
        &&
        exception.StackTrace.Contains("System.Web.HttpApplication.ExecuteStep");
}
Run Code Online (Sandbox Code Playgroud)