问题 ASP.NET Web API +长时间运行操作取消


有没有办法在ASP.NET Web API beta中弄清楚HTTP请求是否被取消(由于任何其他原因而被用户中止)?我正在寻找机会获得一种开箱即用的取消令牌,它会发出请求被中止的信号,因此长时间运行的操作也应该中止。

可能的相关问题 - CancellationTokenModelBinder类的用例。为取消令牌设置单独的绑定器的原因是什么?


2329
2018-03-18 21:28


起源



答案:


你可以检查一下 Response.IsClientConnected 不时看看浏览器是否仍然连接到服务器。


7
2018-03-19 08:20



这似乎来自ASP.NET MVC堆栈,而不是来自ASP.NET MVC Web API。如果错误请纠正我。 - pavel.baravik
这是来自核心ASP.Net引擎。仅适用于IIS集成管道(不是VS调试模式)。 MSDN涵盖了一个很好的文档: msdn.microsoft.com/en-us/library/... - Kamyar Nazeri
谢谢你的建议。这似乎是唯一的选择,我可能会在此基础上构建解决方案。 - pavel.baravik


答案:


你可以检查一下 Response.IsClientConnected 不时看看浏览器是否仍然连接到服务器。


7
2018-03-19 08:20



这似乎来自ASP.NET MVC堆栈,而不是来自ASP.NET MVC Web API。如果错误请纠正我。 - pavel.baravik
这是来自核心ASP.Net引擎。仅适用于IIS集成管道(不是VS调试模式)。 MSDN涵盖了一个很好的文档: msdn.microsoft.com/en-us/library/... - Kamyar Nazeri
谢谢你的建议。这似乎是唯一的选择,我可能会在此基础上构建解决方案。 - pavel.baravik


我想总结一下。似乎有效的唯一方法是检查Response.IsClientConnected。 这里有关于舞台背后的一些技术细节: 这里 和 这里 这种方法有一些缺陷:

  • 仅在IIS下工作(没有自托管,没有开发服务器);
  • 根据一些SO的答案可能会很慢(在客户端断开后不立即做出反应): 这里;
  • 有关此通话费用的注意事项: 这里

最后,我提出了以下代码,将基于IsClientConnected的CancellationToken注入Web API控制器:

    public class ConnectionAbortTokenAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
    private readonly string _paramName;
    private Timer _timer;
    private CancellationTokenSource _tokenSource;
    private CancellationToken _token;

    public ConnectionAbortTokenAttribute(string paramName)
    {
        _paramName = paramName;
    }

    public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        object value;
        if (!actionContext.ActionArguments.TryGetValue(_paramName, out value))
        {
            // no args with defined name found
            base.OnActionExecuting(actionContext);
            return;
        }

        var context = HttpContext.Current;
        if (context == null)
        {
            // consider the self-hosting case (?)
            base.OnActionExecuting(actionContext);
            return;
        }

        _tokenSource = new CancellationTokenSource();
        _token = _tokenSource.Token;
        // inject
        actionContext.ActionArguments[_paramName] = _token;
        // stop timer on client disconnect
        _token.Register(() => _timer.Dispose());

        _timer = new Timer
        (
            state =>
            {
                if (!context.Response.IsClientConnected)
                {
                    _tokenSource.Cancel();
                }
            }, null, 0, 1000    // check each second. Opts: make configurable; increase/decrease.
        );

        base.OnActionExecuting(actionContext);
    }

    /*
     * Is this guaranteed to be called?
     * 
     * 
     */
    public override void OnActionExecuted(System.Web.Http.Filters.HttpActionExecutedContext actionExecutedContext)
    {
        if(_timer != null)
            _timer.Dispose();

        if(_tokenSource != null)
            _tokenSource.Dispose();

        base.OnActionExecuted(actionExecutedContext);
    }
}

8
2018-03-19 15:43



还有一个选项可以使用SignalR和类似的解决方案,但它们对我来说有点超出范围。 - pavel.baravik


如果您将CancellationToken添加到控制器方法中,它将由框架自动注入,当客户端调用xhr.abort()时,令牌将自动取消

类似的东西

public Task<string> Get(CancellationToken cancellationToken = default(CancellationToken))

对于MVC,您也可以参考

HttpContext.Current.Response.IsClientConnected
HttpContext.Response.ClientDisconnectedToken

对于.NetCore

services.AddTransient<ICustomInterface>(provider => { 
     var accessor = provider.GetService<IHttpContextAccessor>);
     accessor.HttpContext.RequestAborted;
  }); 

0
2017-11-24 00:15