Asp.net WebAPI: Aborted (cancelled) requests

12,952

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.

Share:
12,952

Related videos on Youtube

cmart
Author by

cmart

Updated on September 15, 2022

Comments

  • cmart
    cmart over 1 year

    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.

  • cmart
    cmart over 8 years
    Thank you! But isn't this supported out of the box? When a CancellationToken is used as last parameter it gets set by the webapi pipeline. Reference: books.google.at/… (I hope this link keeps working)
  • Paul Turner
    Paul Turner over 8 years
    Then it looks like I've been working on old information.
  • cmart
    cmart over 8 years
    Well, your idea was implemented! There are really clever guys thinking about these topics at microsoft, be proud!