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

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

错误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() 

错误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) 

看着stacktrace,我看到这个exception是从这里抛出的: https : //github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Http.WebHost/HttpControllerHandler.cs# L413

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

它似乎是在用户代码之外…

笔记:

  • 我正在使用ASP.NET Web API 2
  • Web API端点是asynchronous和非asynchronous方法的混合体。
  • 无论我在哪里添加错误日志logging,我都无法捕捉用户代码中的exception
    • Global.asax Applicaiton_Error
    • TaskScheduler.UnobservedTaskException
    • ELMAH错误筛选void ErrorLog_Filtering ( https://code.google.com/p/elmah/wiki/ErrorFiltering )

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

最终,问题在于,在这种情况下,我们将取消的任务返回给ASP.NET,而ASP.NET将取消的任务视为未处理的exception(它将应用程序事件日志中的问题logging下来)。

同时,您可以尝试类似下面的代码。 它添加了一个顶级消息处理程序,在取消令牌触发时删除内容。 如果响应没有内容,则不应触发该错误。 发生这种情况的可能性依然很小,因为在消息处理程序检查取消令牌之后,但在更高级别的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; } } 

当为WebApi实现exceptionlogging器时,build议扩展System.Web.Http.ExceptionHandling.ExceptionLogger类,而不是创build一个ExceptionFilter。 WebApi内部不会调用已取消请求的ExceptionLoggers的Log方法(但是,exceptionfilter将获得它们)。 这是devise。

 HttpConfiguration.Services.Add(typeof(IExceptionLogger), myWebApiExceptionLogger); 

您可以尝试通过web.config更改默认的TPL任务exception处理行为 :

 <configuration> <runtime> <ThrowUnobservedTaskExceptions enabled="true"/> </runtime> </configuration> 

然后在您的Web应用程序中有一个static类(带有static构造函数),它将处理AppDomain.UnhandledException

但是,在您甚至有机会使用您的代码处理它之前, 这个exception实际上是在ASP.NET Web API运行时的某个地方处理的。

在这种情况下,您应该能够将其作为第一个机会exception捕获,并带有AppDomain.CurrentDomain.FirstChanceException , 这里是如何实现的 。 我知道这可能不是你正在寻找的。

这是另一个解决此问题的解决方法。 只要在捕获OperationCanceledException的OWINpipe道的开头添加一个自定义的OWIN中间件:

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

我有时在我的Web API 2应用程序中得到相同的2个exception,但是我可以用Global.asax.csApplication_Error方法来捕获它们,并使用一个通用的exceptionfilter 。

有趣的是,我更喜欢不去捕捉这些exception,因为我总是logging所有可以使应用程序崩溃的未处理exception(然而,这两个对我来说是不相关的,显然不是或者至less不应该崩溃它,但我可能是错的)。 我怀疑这些错误显示由于一些超时过期或显式取消从客户端,但我会期望他们在ASP.NET框架内处理,不作为未处理的exception传播外面。

我们已经收到了同样的exception,我们试图使用@ dmatson的解决方法,但是我们仍然会得到一些exception。 我们直到最近处理它。 我们注意到一些Windows日志以惊人的速度增长。

错误文件位于:C:\ Windows \ System32 \ LogFiles \ HTTPERR

大部分错误都是为了“Timer_ConnectionIdle”。 我周围search,似乎即使networkingAPI调用完成,连接仍然持续了两分钟,通过原来的连接。

然后,我想我们应该尝试closures响应中的连接,看看会发生什么。

我添加了response.Headers.ConnectionClose = true; 到SendAsync MessageHandler,从我可以告诉客户正在closures连接,我们不再遇到这个问题。

我知道这不是最好的解决scheme,但它适用于我们的情况。 我也是非常确定的性能明智的,这不是你想要做的,如果你的API从同一个客户端连续多次调用。