我怎么知道 HttpClient 超时了?

据我所知,没有办法知道是否发生了特定的超时。我是找错地方了,还是错过了更重要的事?

string baseAddress = "http://localhost:8080/";
var client = new HttpClient()
{
BaseAddress = new Uri(baseAddress),
Timeout = TimeSpan.FromMilliseconds(1)
};
try
{
var s = client.GetAsync("").Result;
}
catch(Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.InnerException.Message);
}

结果是:

发生了一个或多个错误。

任务取消了。

178012 次浏览

来自 http://msdn.microsoft.com/en-us/library/system.net.http.httpclient.timeout.aspx

域名系统(DNS)查询可能需要长达15秒才能返回或超时。如果请求包含需要解析的主机名,并将 Timeout 设置为小于15秒的值,则在 抛出 WebException 以指示请求超时之前可能需要15秒或更长时间。

然后可以访问 Status属性,请参见 WebExceptionStatus

您需要等待 GetAsync方法。然后,如果超时,它将抛出一个 TaskCanceledException。此外,GetStringAsyncGetStreamAsync在内部处理超时,因此它们永远不会抛出。

string baseAddress = "http://localhost:8080/";
var client = new HttpClient()
{
BaseAddress = new Uri(baseAddress),
Timeout = TimeSpan.FromMilliseconds(1)
};
try
{
var s = await client.GetAsync();
}
catch(Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.InnerException.Message);
}

我在重复同样的问题,这真的很烦人。我发现这些很有用:

HttpClient ——处理聚合异常

HttpClient 中的 Bug。 GetAsync 应该抛出 WebException,而不是 TaskCanceledException

一些代码以防链接无处可去:

var c = new HttpClient();
c.Timeout = TimeSpan.FromMilliseconds(10);
var cts = new CancellationTokenSource();
try
{
var x = await c.GetAsync("http://linqpad.net", cts.Token);
}
catch(WebException ex)
{
// handle web exception
}
catch(TaskCanceledException ex)
{
if(ex.CancellationToken == cts.Token)
{
// a real cancellation, triggered by the caller
}
else
{
// a web request timeout (possibly other things!?)
}
}

我发现确定服务调用是否超时的最佳方法是使用取消令牌,而不是 HttpClient 的超时属性:

var cts = new CancellationTokenSource();
cts.CancelAfter(timeout);

然后在服务调用期间处理取消异常..。

catch(TaskCanceledException)
{
if(cts.Token.IsCancellationRequested)
{
// Timed Out
}
else
{
// Cancelled for some other reason
}
}

当然,如果超时发生在服务端,那么 WebException 应该能够处理。

基本上,您需要捕获 OperationCanceledException并检查传递给 SendAsync(或 GetAsync,或您正在使用的 HttpClient方法)的取消令牌的状态:

  • 如果请求被取消(IsCancellationRequested为 true) ,则表示请求真的被取消了
  • 如果没有,则表示请求超时

当然,这并不是很方便... 最好接收一个 TimeoutException以防超时。我在这里提出了一个基于自定义 HTTP 消息处理程序的解决方案: 使用 HttpClient 进行更好的超时处理

从现在开始。NET 5,实施方式已经改变.HttpClient仍然抛出一个 TaskCanceledException,但现在将一个 TimeoutException包装为 InnerException。因此,您可以很容易地检查请求是否被取消或超时(从链接的博客文章中复制的代码示例) :

try
{
using var response = await _client.GetAsync("http://localhost:5001/sleepFor?seconds=100");
}
// Filter by InnerException.
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
{
// Handle timeout.
Console.WriteLine("Timed out: "+ ex.Message);
}
catch (TaskCanceledException ex)
{
// Handle cancellation.
Console.WriteLine("Canceled: " + ex.Message);
}