使用语句中的 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 导致任务被取消

我知道,如果在异步调用完成之前释放了 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;