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

Posted

技术标签:

【中文标题】ASP.NET Web API + 长时间运行的操作取消【英文标题】:ASP.NET Web API + Long running operation cancellation 【发布时间】:2012-04-03 11:34:37 【问题描述】:

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

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

【问题讨论】:

【参考方案1】:

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

【讨论】:

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

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

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

最后我想出了以下代码,将基于 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);
    

【讨论】:

还有一个使用 SignalR 和类似解决方案的选项,但它们对我来说有点超出了范围。【参考方案3】:

如果你在控制器方法中添加了CancellationToken,它会被框架自动注入,当客户端调用xhr.abort()时,token会被自动取消

类似

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;
  ); 

【讨论】:

以上是关于ASP.NET Web API + 长时间运行的操作取消的主要内容,如果未能解决你的问题,请参考以下文章

部署在 IIS 上的 ASP.NET Core 对长时间运行的请求返回 502 错误

asp.net 页面在服务器上需要很长时间的加载时间

如何异步运行长时间运行的操作并在 ASP.Net Ajax 中为用户显示加载并轮询结果?

如何使用javascript在asp.net上停止正在进行的回发(长时间运行)?

长时间运行的 ASP.NET MVC 核心控制器 HTTPPost 方法超时

在 ASP.Net MVC5 中管理长时间运行的进程