简体   繁体   English

使用语句中的HttpClient导致任务被取消

[英]HttpClient in using statement causes Task cancelled

I created a FileResult : IHttpActionResult webapi return type for my api calls. 我为我的api调用创建了一个FileResult : IHttpActionResult webapi返回类型。 The FileResult downloads a file from another url and then returns the stream to the client. FileResult从另一个URL下载文件,然后将流返回给客户端。

Initially my code had a using statement like below: 最初我的代码有一个如下所示的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)
    {...}
}

However this would intermittently cause a TaskCanceledException . 但是,这会间歇性地导致TaskCanceledException I know that if the HttpClient is disposed before the asychronous call is finished the Task's state will change to canceled. 我知道如果在异步调用完成之前处理了HttpClient,Task的状态将变为取消。 However since I use an await in: Content = new System.Net.Http.StreamContent(await httpClient.GetStreamAsync(this.filePath)) that should prevent the HttpClient from being disposed off in the middle of the task completion. 但是因为我使用了awaitContent = new System.Net.Http.StreamContent(await httpClient.GetStreamAsync(this.filePath)) ,它应该阻止HttpClient在任务完成过程中被处理掉。

Why does that task get canceled? 为什么该任务被取消? It is not because of a timeout since this has happened on the smallest requests and doesn't always occur on large requests. 这不是因为超时,因为这发生在最小的请求上,并不总是发生在大请求上。

When I removed the using statement the code worked properly: 当我删除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)
    {...}
}

Any idea why the using caused the issue? 知道为什么使用导致了这个问题吗?

I know that if the HttpClient is disposed before the asychronous call is finished the Task's state will change to canceled. 我知道如果在异步调用完成之前处理了HttpClient,Task的状态将变为取消。 However since I use an await in: Content = new System.Net.Http.StreamContent(await httpClient.GetStreamAsync(this.filePath)) that should prevent the HttpClient from being disposed off in the middle of the task completion. 但是因为我使用了await:Content = new System.Net.Http.StreamContent(等待httpClient.GetStreamAsync(this.filePath)),它应该阻止HttpClient在任务完成过程中被处理掉。

But what does that task do? 但是这项任务做了什么? It gets the stream. 它得到了流。 So, your code ends up with a Stream that may or may not be completely read when it closes the HttpClient . 因此,您的代码最终会得到一个Stream ,当它关闭HttpClient时可能会或可能不会被完全读取。

HttpClient is specifically designed for reuse (and simultaneous use), so I recommend removing the using completely and moving the HttpClient declaration to a static class member. HttpClient专门用于重用(和同时使用),因此我建议完全删除using并将HttpClient声明移动到static类成员。 But if you want to close and reopen the clients, you should be able to get it working by reading the stream entirely into memory before closing the HttpClient . 但是,如果要关闭并重新打开客户端,则应该能够通过在关闭HttpClient之前将流完全入内存来使其工作。

I had a similar issue with Task Canceled exceptions. 我遇到了类似于Task Canceled异常的问题。 If you try catching AggregateException or having a catch all Exception block underneath your WebException , you may well find that you catch it, with one exception with the entry stating "A task was canceled" 如果您尝试捕获AggregateException或在WebException下面捕获所有Exception块,您可能会发现它捕获它,但有一个例外,条目说明“任务已被取消”

I did some investigation and found that the AggregateException is quite misleading as described in various threads; 我做了一些调查,发现AggregateException很容易产生误导,如各种线程所述;

Setting HttpClient to a too short timeout crashes process 将HttpClient设置为太短的超时会导致进程崩溃

How can I tell when HttpClient has timed out? 如何判断HttpClient何时超时?

Bug in httpclientgetasync should throw webexception not taskcanceledexception httpclientgetasync中的错误应该抛出webexception而不是taskcanceledexception

I ended up changing my code to set an explicit timeout (where asyncTimeoutInMins is read from the app.config file); 我最终更改了我的代码以设置显式超时(从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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM