Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asp.net WebAPI: Aborted (cancelled) requests

For starters, this is a discussion if anyone of you guys does such a thing as request cancellation in WebAPI controllers (probably also applicable for MVC).

What I mean in particular is a scenario like the following: A client (browser typically) starts a request, navigates away or more general, aborts the request for any reason. Now, the request is aborted on the client side and will no longer be considered. But on the server side the request is still executing and typically might be doing two things especially interesting:

  • Make a (heavy) DB-Query
  • Make a (heavy) service call to another service

And all for nothing in the end (at least when it's a side-effect free read operation at least).

Does somebody handle cancellation of the ongoing query/service call in such a case?

What I know is that a CancellationToken can be passed in the API-Controller (even though I couldn't get it working so that cancellation was really requested when aborting from the client). This CancellationToken, in theory, would need to be passed down to all lower layers to handle a probable cancellation of database and service calls.

like image 744
cmart Avatar asked Jul 31 '15 11:07

cmart


1 Answers

Whilst at time of writing WebAPI does not directly support cancellation, the OWIN API absolutely does. If you're using WebAPI 2.0, you'll be running on top of OWIN and will be able to access the context via the Microsoft wrappers using the GetOwinContext() extension method.

Conveniently the cancellation is propagated using a CancellationToken value exposed via the CallCancelled property of the OwinRequest. You can put this all together to get the token inside a controller method:

public async Task Get()
{
    var cancellation = Request.GetOwinContext().Request.CallCancelled;

    await database.FooAsync(cancellation);
}

That's pretty ugly though. You'll have to make this call in every method that needs to handle cancellation and it doesn't work nicely with the proposed future where WebAPI will give you this CancellationToken. Instead, wouldn't it be nicer if we can make this into a parameter?

public async Task Get(CancellationToken cancellation)
{
    await database.FooAsync(cancellation);
}

To do this, you can create a custom parameter binding that grabs the CancellationToken from the OWIN context:

public class OwinCancellationTokenBinding : HttpParameterBinding
{
    public OwinCancellationTokenBinding(HttpParameterDescriptor parameter)
        : base(parameter)
    {
    }

    public override Task ExecuteBindingAsync(
        ModelMetadataProvider metadataProvider, 
        HttpActionContext actionContext,
        CancellationToken cancellationToken)
    {
        actionContext.ActionArguments[Descriptor.ParameterName]
            = actionContext.Request.GetOwinContext().Request.CallCancelled;

        return Task.FromResult<object>(null);
    }
}

To use this, you can register the binding with the HttpConfiguration using its ParameterBindingRules collection:

config.ParameterBindingRules.Add(p
    => p.ParameterType == typeof(CancellationToken)
    ? new OwinCancellationTokenBinding (p)
    : null);

This rule matches any parameter of type CancellationToken. You can create any rule here that matches the parameters you want this value provided for.

like image 107
Paul Turner Avatar answered Sep 28 '22 04:09

Paul Turner