Raf*_*afi 9 c# using-statement httpclient task-parallel-library asp.net-web-api
我FileResult : IHttpActionResult为api调用创建了一个webapi返回类型.FileResult从另一个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)
{...}
}
Run Code Online (Sandbox Code Playgroud)
然而,这会间歇性地导致TaskCanceledException.我知道如果在异步调用完成之前处理了HttpClient,Task的状态将变为取消.但是因为我使用了await: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)
{...}
}
Run Code Online (Sandbox Code Playgroud)
知道为什么使用导致了这个问题吗?
Ste*_*ary 11
我知道如果在异步调用完成之前处理了HttpClient,Task的状态将变为取消.但是因为我使用了await:Content = new System.Net.Http.StreamContent(等待httpClient.GetStreamAsync(this.filePath)),它应该阻止HttpClient在任务完成过程中被处理掉.
但是这项任务做了什么?它得到了流.因此,您的代码最终Stream会以关闭时可能会或可能不会完全读取的代码结束HttpClient.
HttpClient是专门为重用(和同时使用)而设计的,所以我建议using完全删除并将HttpClient声明移动到static类成员.但是如果你想关闭并重新打开客户端,你应该能够通过阅读来使它工作在关闭之前将流完全入内存HttpClient.
我遇到了类似于Task Canceled异常的问题.如果你尝试捕获AggregateException或捕获你Exception下面的所有块WebException,你可能会发现你捕获它,但有一个例外,条目是"任务被取消"
我做了一些调查,发现它AggregateException在各种线索中都有误导性;
httpclientgetasync中的错误应该抛出webexception而不是taskcanceledexception
我最终更改了我的代码以设置显式超时(asyncTimeoutInMins从app.config文件中读取);
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;
Run Code Online (Sandbox Code Playgroud)