12

Is there a way to figure out in ASP.NET Web API beta whether the HTTP request was cancelled (aborted by user of for any another reason)? I'm looking for opportunity to have a kind of cancellation token out-of-the-box that will signal that the request is aborted and therefore long-running ops should be aborted as well.

Possible related question - the use case for the CancellationTokenModelBinder class. What's the reason to have a separate binder for cancellation token?

3 Answers 3

9

You could check Response.IsClientConnected from time to time to see if the browser is still connected to the server.

Sign up to request clarification or add additional context in comments.

3 Comments

This seems to be from ASP.NET MVC stack, not from ASP.NET MVC Web API. Please correct me if wrong.
This is from core ASP.Net engine. Only works with IIS integration pipeline (not VS debug mode). MSDN covers a good documentation: msdn.microsoft.com/en-us/library/…
thank you for suggestion. This seems to be the only option, I'll probably build solution basing on this.
9

I'd like to sum-up a bit. The only approach that seem to work is checking Response.IsClientConnected. Here some technical details regarding what is going behind the stage: here and here This approach has some flaws:

  • Works only under IIS (no self-hosting, no Dev Server);
  • According to some SO answers may be slow (do not react immediately after client disconnected): here;
  • There are considerations regarding this call cost: here

At the end I came up with the following piece of code to inject CancellationToken based on IsClientConnected into the Web API controller:

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

1 Comment

Also there is an option to use SignalR and similar solutions, but they're a bit out of scope for me.
1

If you added CancellationToken in to controller methods, it will be automatically injected by the framework, and when a client calls xhr.abort() the token will be automatically cancelled

Something similar to

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

For MVC you can also refer to

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

For .NetCore

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.