区分超时与用户取消

HttpClient具有内置超时function(尽管全部是异步的,即超时可以被认为是与http请求function正交,因此可以由通用异步实用程序处理,但除此之外)并且当超时启动时,它将抛出TaskCanceledException (包装在AggregateException )。

TCE包含一个等于CancellationToken.None

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

是否还有一种方法, 通过仅查看抛出的exception ,找出超时是否取消了请求,而不必让我自己的CancellationToken可以访问检查exception的代码?

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

编辑为了使问题更加清晰,通过访问原始的CancellationTokenSource ,可以很容易地区分超时和用户取消:

origCancellationTokenSource.IsCancellationRequested == true

从exception中获取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 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); } 

接受的答案肯定是理论上应该如何工作,但遗憾的是,在实践中, IsCancellationRequested并未(可靠地)设置在附加到exception的令牌上:

取消HttpClient请求 – 为什么TaskCanceledException.CancellationToken.IsCancellationRequested为false?

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

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

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