当自托管 OWIN 服务器关闭时,如何向 Web API 操作发出取消信号?

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

我有一个基于 OWIN 的 ASP.NET Web API 托管在 Windows 服务中。我的大多数 ApiController 操作都是异步的,并且接受 CancellationToken 参数:

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

使用 Web 的内置请求取消功能 API,如果客户端取消请求,token 会收到信号,_dataSource 会适当处理并抛出 OperationCanceledException.

到目前为止,太棒了。

但是当我的主机进程 终止 (即 Windows 服务停止)时,token 没有发出信号并且取消 - 并且 -纾困过程并不顺利。

我知道 OWIN 环境字典的 host.onAppDisposing 属性,并且我已经深入研究了 的源代码Microsoft.Owin[.*]Microsoft.AspNet.WebApi.* 包试图找出 GetSomeDataAsynctoken 争论来自,但我不确定如何将这些部分连接在一起。

我想做类似的事情

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

但我不确定如何让 _cts 成为 CancellationTokens 的源泉,这些 CancellationTokens 可以满足我的行为,同时不会破坏 运行良好的请求取消功能。

我认为 CancellationTokenSource.CreateLinkedTokenSource() 可能会有用,但我不知道如何将各个部分组合在一起。

你能帮忙吗?谢谢!

host.onAppDisposing 当您对从 WebApp.Start 返回的值调用 Dispose 时触发。

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 仅与请求断开令牌相关联(例如 owin.CallCancelled)。通过中间件或其他方式,您可以将其替换为也连接到 host.onAppDisposing.

的链接 TCS

类似于:

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