web-dev-qa-db-ja.com

HttpClientでステートメントを使用すると、タスクがキャンセルされます

API呼び出し用に_FileResult : IHttpActionResult_ 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)
    {...}
}
_

ただし、これにより断続的にTaskCanceledExceptionが発生します。非同期呼び出しが終了する前にHttpClientが破棄されると、タスクの状態がキャンセルに変わることを知っています。ただし、await in: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)
    {...}
}
_

使用が問題を引き起こした理由は何ですか?

9
Rafi

非同期呼び出しが終了する前にHttpClientが破棄されると、タスクの状態がキャンセルに変わることを知っています。ただし、await in:Content = new System.Net.Http.StreamContent(await httpClient.GetStreamAsync(this.filePath))を使用しているため、タスクの完了中にHttpClientが破棄されるのを防ぐ必要があります。

しかし、そのタスクは何をしますか?ストリームを取得します。したがって、コードはStreamで終わり、HttpClientを閉じるときに完全に読み取られる場合と読み取られない場合があります。

HttpClientは再利用(および同時使用)用に特別に設計されているため、usingを完全に削除し、HttpClient宣言をstaticクラスメンバーに移動することをお勧めします。ただし、クライアントを閉じて再度開く場合は、HttpClient

11
Stephen Cleary

タスクキャンセルの例外についても同様の問題が発生しました。 AggregateExceptionをキャッチしようとしたり、Exceptionの下にあるWebExceptionブロックをすべてキャッチしようとしたりすると、「タスクキャンセルされた"

調査を行ったところ、さまざまなスレッドで説明されているように、AggregateExceptionは非常に誤解を招くことがわかりました。

HttpClientを短すぎるタイムアウトに設定するとプロセスがクラッシュします

HttpClientがタイムアウトしたことを確認するにはどうすればよいですか?

httpclientgetasyncのバグは、taskcanceledexceptionではなくwebexceptionをスローする必要があります

明示的なタイムアウトを設定するようにコードを変更することになりました(ここで、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;
5
Mr Moose