为了重用
HttpClient
打开的 TCP 连接,您必须为所有请求共享一个实例。
这意味着我们不能简单地用不同的设置(例如超时或标题)实例化
HttpClient
。
我们如何共享连接并同时使用不同的设置?这非常简单,实际上是默认设置,使用较旧的
HttpWebRequest
和 WebClient
基础设施。
请注意,在发出请求之前简单地设置
HttpClient.Timeout
不是线程安全的,并且不会在并发应用程序(例如 ASP.NET 网站)中工作。
在幕后,
HttpClient
只是使用取消标记来实现超时行为。如果你想根据请求改变它,你可以直接做同样的事情:
using var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(30));
await httpClient.GetAsync("http://www.google.com", cts.Token);
请注意,
HttpClient
的默认超时时间为 100 秒,即使您在请求级别设置了更高的值,该请求仍会在该时间点被取消。要解决这个问题,请在 HttpClient
上设置一个“最大”超时,它可以是无限的:
httpClient.Timeout = System.Threading.Timeout.InfiniteTimeSpan;
接受的答案很好,但我想为将来寻找这个的人提供另一个场景。在我的例子中,我已经在使用 CancellationTokenSource,它会在用户选择取消时取消其令牌。因此,在那种情况下,您仍然可以通过使用 CancellationTokenSource 的 CreateLinkedTokenSource 方法来使用此技术。因此,在我的场景中,http 操作将因超时或用户干预而取消。这是一个示例:
public async static Task<HttpResponseMessage> SendRequest(CancellationToken cancellationToken)
{
var ctsForTimeout = new CancellationTokenSource();
ctsForTimeout.CancelAfter(TimeSpan.FromSeconds(5));
var cancellationTokenForTimeout = ctsForTimeout.Token;
using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cancellationTokenForTimeout))
{
try
{
return await httpClient.GetAsync("http://asdfadsf", linkedCts.Token);
}
catch
{
//just for illustration purposes
if (cancellationTokenForTimeout.IsCancellationRequested)
{
Console.WriteLine("timeout");
}
else if (cancellationToken.IsCancellationRequested)
{
Console.WriteLine("other cancellation token cancelled");
}
throw;
}
}
}
如果您已经在使用
CancellationToken
可能来自异步 API 端点,您可以使用 CancellationToken
将它与另一个 CancellationTokenSource.CreateLinkedTokenSource
结合。
举个例子:
public async Task<HttpResponseMessage> SendRequestWithTimeout(HttpRequestMessage request, Timespan timeout, CancellationToken ct){
var timeoutCt = new CancellationTokenSource(timeout).Token;
var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(ct, timeoutCt).Token;
return await httpClient.SendAsync(request, linkedCts);
}
.NET 6(及更高版本)现在包含您可以使用的
Task.WaitAsync(TimeSpan)
方法。
await httpClient.SendAsync(request).WaitAsync(TomeSpan.FromSeconds(5));
获取将在该任务完成或 指定超时到期。