使用语句中的 HttpClient 导致任务被取消
本文关键字:任务 取消 HttpClient 语句 | 更新日期: 2023-09-27 18:17:36
我为我的 api 调用创建了一个FileResult : IHttpActionResult
webapi 返回类型。文件结果从另一个 URL 下载文件,然后将流返回到客户端。
最初我的代码有一个using
语句,如下所示:
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
try
{
HttpResponseMessage response;
using (var httpClient = new HttpClient())
{
response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new System.Net.Http.StreamContent(
await httpClient.GetStreamAsync(this.filePath))
};
}
return response;
}
catch (WebException exception)
{...}
}
但是,这会间歇性地导致TaskCanceledException
。我知道,如果在异步调用完成之前释放了 HttpClient,则任务的状态将更改为已取消。但是,由于我在:中使用了等待:Content = new System.Net.Http.StreamContent(await httpClient.GetStreamAsync(this.filePath))
这应该防止 HttpClient 在任务完成过程中被释放。
为什么该任务会被取消?这不是因为超时,因为这发生在最小的请求上,并不总是发生在大请求上。
当我删除using
语句时,代码工作正常:
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
try
{
HttpResponseMessage response;
var httpClient = new HttpClient();
response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new System.Net.Http.StreamContent(
await httpClient.GetStreamAsync(this.filePath))
};
return response;
}
catch (WebException exception)
{...}
}
知道为什么使用导致问题吗?
我知道,如果在异步调用完成之前释放了 HttpClient,则任务的状态将更改为已取消。但是,由于我在以下位置使用了await:Content = new System.Net.Http.StreamContent(await httpClient.GetStreamAsync(this.filePath((,这应该可以防止HttpClient在任务完成过程中被释放。
但是这项任务有什么作用呢?它获取流。因此,您的代码最终会得到一个Stream
,当它关闭HttpClient
时,可能会也可能不会完全读取。
HttpClient
是专门为重用(和同时使用(而设计的,因此我建议完全删除using
并将 HttpClient
声明移动到 static
类成员。但是,如果要关闭并重新打开客户端,则应该能够通过在关闭HttpClient
之前将流完全读取到内存中来使其工作。
我在任务取消异常方面遇到了类似的问题。如果您尝试捕获AggregateException
或将捕获所有Exception
块放在WebException
下方,您可能会发现您抓住了它,但有一个例外,该条目指出"任务已取消">
我做了一些调查,发现AggregateException
非常具有误导性,如各种线程中所述;
将 HttpClient 设置为太短的超时崩溃过程
如何知道 HttpClient 何时超时?
httpclientgetasync 中的错误应该抛出 webexception 而不是任务取消异常
我最终更改了我的代码以设置显式超时(从 app.config 文件中读取asyncTimeoutInMins
(;
string jsonResponse = string.Empty;
try
{
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(Properties.Settings.Default.MyWebService);
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.Timeout = new TimeSpan(0, asyncTimeoutInMins, 0);
HttpResponseMessage response;
response = await httpClient.GetAsync("/myservice/resource");
// Check the response StatusCode
if (response.IsSuccessStatusCode)
{
// Read the content of the response into a string
jsonResponse = await response.Content.ReadAsStringAsync();
}
else if (response.StatusCode == HttpStatusCode.Forbidden)
{
jsonResponse = await response.Content.ReadAsStringAsync();
Logger.Instance.Warning(new HttpRequestException(string.Format("The response StatusCode was {0} - {1}", response.StatusCode.ToString(), jsonResponse)));
Environment.Exit((int)ExitCodes.Unauthorised);
}
else
{
jsonResponse = await response.Content.ReadAsStringAsync();
Logger.Instance.Warning(new HttpRequestException(string.Format("The response StatusCode was {0} - {1}", response.StatusCode.ToString(), jsonResponse)));
Environment.Exit((int)ExitCodes.ApplicationError);
}
}
}
catch (HttpRequestException reqEx)
{
Logger.Instance.Error(reqEx);
Console.WriteLine("HttpRequestException : {0}", reqEx.InnerException.Message);
Environment.Exit((int)ExitCodes.ApplicationError);
}
catch (Exception ex)
{
Logger.Instance.Error(ex);
throw;
}
return jsonResponse;