28

HttpClient具有内置超时功能(尽管都是异步的,即超时可以被认为与 http 请求功能正交,因此可以由通用异步实用程序处理,但除此之外)并且当超时开始时,它会抛出一个TaskCanceledException(包裹在一个AggregateException)。

TCE 包含一个CancellationToken等于CancellationToken.None

现在,如果我提供自己的HttpClientaCancellationToken并使用它在操作完成(或超时)之前取消操作,我会得到完全相同的TaskCanceledException,再次使用 a CancellationToken.None

有没有办法通过只查看抛出的异常来确定超时是否取消了请求,而不必让我自己CancellationToken访问检查异常的代码?

PS这可能是一个错误并且CancellationToken以某种方式错误地修复了CancellationToken.None吗?在使用自定义 CancellationToken 取消的情况下,我希望TaskCanceledException.CancellationToken等于该自定义令牌。

编辑 为了使问题更清楚一点,通过访问原始CancellationTokenSource,很容易区分超时和用户取消:

origCancellationTokenSource.IsCancellationRequested == true

CancellationToken从异常中获取虽然给出了错误的答案:

((TaskCanceledException) e.InnerException).CancellationToken.IsCancellationRequested == false

由于受欢迎的需求,这里有一个最小的例子:

public void foo()
{
    makeRequest().ContinueWith(task =>
    {
        try
        {
            var result = task.Result;
            // do something with the result;
        }
        catch (Exception e)
        {
            TaskCanceledException innerException = e.InnerException as TaskCanceledException;
            bool timedOut = innerException != null && innerException.CancellationToken.IsCancellationRequested == false;

            // Unfortunately, the above .IsCancellationRequested
            // is always false, no matter if the request was
            // cancelled using CancellationTaskSource.Cancel()
            // or if it timed out
        }
    });
}

public Task<HttpResponseMessage> makeRequest()
{
    var cts = new CancellationTokenSource();
    HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(10) };
    HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "url");

    passCancellationTokenToOtherPartOfTheCode(cts);
    return client.SendAsync(httpRequestMessage, cts.Token);
}
4

2 回答 2

6

公认的答案当然是理论上应该如何工作,但不幸的是,在实践IsCancellationRequested中并没有(可靠地)设置附加到异常的令牌:

取消 HttpClient 请求 - 为什么 TaskCanceledException.CancellationToken.IsCancellationRequested 为假?

于 2015-03-30T18:01:20.867 回答
4

是的,它们都返回相同的异常(可能是因为在内部也使用了令牌超时),但可以通过这样做很容易地找出它:

   catch (OperationCanceledException ex)
            {
                if (token.IsCancellationRequested)
                {
                    return -1;
                }

                return -2;
            }

所以基本上如果你遇到异常但你的令牌没有被取消,那么这是一个常规的 http 超时

于 2013-03-01T02:03:09.843 回答