异步Web Api控制器 - 如何处理取消?

时间:2015-05-08 20:48:14

标签: c# asp.net-web-api owin

我正在运行一个Owin Self托管网络Api,其典型控制器如下:

    [HttpGet]
    [Route("refresh/{secId}")]
    [ResponseType(typeof(int))]
    public async Task<IHttpActionResult> RefreshElement(int secId)
    {
        var count = await _db.Refresh(secId);

        if (count == 0)
            return NotFound();

        return Ok(count);
    }

假设_db.Refresh()长时间运行(几秒),有时会引发异常。

我设法重现的问题是:

  • 请求命中方法,_db.Refresh被解雇
  • 请求被取消(套接字关闭)

不再等待_db.Refresh的结果 - 因为我看到它何时返回异常,当任务为GCd时,它会通过TPL未观察到的异常处理显示...

也许是因为这种互动,.net团队改变了未处理的异常政策,不是为了拆除流程(我认为是4.5以后)......那么这个问题的优秀模式是什么?特别是对于使用OWIN的自托管WebApi - 因为我仍然将未观察到的异常记录为FATAL:)

我可以让_db.Refresh()接受一个取消令牌,但是我如何/何时在自我主机webapi中设置取消令牌以取消连接/取消?

2 个答案:

答案 0 :(得分:13)

也许这并不能完全回答你的问题,但我相信它会回答这最后一部分:

  

我可以让_db.Refresh()接受一个取消令牌,但是我如何/何时在自我主机webapi中设置取消令牌以取消连接/取消?

如果在操作方法中添加CancellationToken参数,框架会在调用时提供一个参数。

[HttpGet]
[Route("refresh/{secId}")]
[ResponseType(typeof(int))]
public async Task<IHttpActionResult> RefreshElement(
    int secId, CancellationToken cancellationToken)
{
    var count = await _db.Refresh(secId, cancellationToken);

    if (count == 0)
        return NotFound();

    return Ok(count);
}

但是请注意,在操作方法退出之前,客户端可能会断开一个小的机会窗口,在这种情况下,您的TPL未观察到的异常仍将发生。

答案 1 :(得分:0)

您可以通过自己实施中间件来处理请求超时。

IOwinContext有一个Request属性,该属性本身包含名为CancellationToken的{​​{1}},可以轮询该请求以查看请求是否已取消:

CallCancelled

注意一般的异常条款是因为我不确定OWIN会在tge请求被取消时抛出TCE,所以无论如何都要确定令牌。

修改

正如@usr向我指出的那样,这个解决方案将处理外部public class CancellationAwareOwinMiddleware : OwinMiddleware { public async Task Invoke(IOwinContext owinContext) { try { await Next.Invoke(owinContext); } catch (TaskCanceledException) { // Handle cancellation. } catch (Exception e) { if (owinContext.Request.CallCancelled.IsCancellationRequested) { // Handle cancellation. } } } } 未被观察到的情况,这是我正在做的一个相当大的假设。如果这是内部任务,我需要重新考虑解决方案。