简体   繁体   中英

How can I signal cancellation to Web API actions when the self-hosted OWIN server shuts down?

I have an OWIN-based ASP.NET Web API hosted in a Windows Service. Most of my ApiController actions are async, and accept CancellationToken parameters:

[Route("data/{id}")]
public async Task<IHttpActionResult> GetSomeDataAsync(int id, CancellationToken token)
{
    try
    {
        using (var _dataSource = ...)
        {
            return Ok(await _dataSource.GetDataAsync(id, token));
        }
    }
    catch (OperationCanceledException ex)
    {
        return StatusCode(HttpStatusCode.NoContent);
    }
}

Using the built-in request-cancellation features of Web API, if the client cancels the request, token is signaled and _dataSource handles it appropriately and throws the OperationCanceledException .

So far, so great.

But when my host process terminates (that is, the Windows Service stops), token isn't signaled and the cancellation-and-bail-out process isn't graceful.

I'm aware of the OWIN environment dictionary's host.onAppDisposing property, and I've dug into the source for the Microsoft.Owin[.*] and Microsoft.AspNet.WebApi.* packages to try and figure out where GetSomeDataAsync 's token argument is coming from, but I'm not sure how to connect the pieces together.

I'd like to do something like

class WebServiceInAWindowsService : ServiceBase
{
    private readonly CancellationTokenSource _cts = new CancellationTokenSource();
    ...
    protected override void OnStop()
    {
        _cts.Cancel();
    }
}

But I'm not sure how to get _cts to be the source of the CancellationTokens that get fed to my actions, while not breaking the request-cancellation feature that's working well.

I'm thinking that CancellationTokenSource.CreateLinkedTokenSource() might be useful, but I'm not seeing how to put the pieces together.

Can you help? Thanks!

host.onAppDisposing is triggered when you call Dispose on the value returned from WebApp.Start .

https://github.com/aspnet/AspNetKatana/blob/9f6e09af6bf203744feb5347121fe25f6eec06d8/src/Microsoft.Owin.Hosting/Engine/HostingEngine.cs#L302-L308

https://github.com/aspnet/AspNetKatana/blob/9f6e09af6bf203744feb5347121fe25f6eec06d8/src/Microsoft.Owin.Hosting/Engine/HostingEngine.cs#L112

GetSomeDataAsync 's is only associated with the request disconnect token by default (eg owin.CallCancelled ). Via middleware or otherwise you can replace it with a linked TCS that's also connected to host.onAppDisposing .

Something like:

app.Use(async (env, next) =>
{
  var reqAbt = env.Get<CancellationToken>("owin.CallCancelled");
  var appAbt = env.Get<CancellationToken>("host.onAppDisposing"); 
  using (linked = CancellationTokenSource.CreateLinkedTokenSource(reqAbt, appAbt))
  {
    env["owin.CallCancelled"] = linked.Token;
    await next();
    env["owin.CallCancelled"] = reqAbt;
  }
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM