HttpClientとHttpClientHandlerはリクエスト間で破棄する必要がありますか?


333

System.Net.Http.HttpClientおよびSystem.Net.Http.HttpClientHandler.NET Framework 4.5のは、IDisposableを実装します(System.Net.Http.HttpMessageInvokerを介して)。

using声明のドキュメントは言います:

原則として、IDisposableオブジェクトを使用する場合は、usingステートメントで宣言してインスタンス化する必要があります。

この回答はこのパターンを使用しています:

var baseAddress = new Uri("http://example.com");
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("foo", "bar"),
        new KeyValuePair<string, string>("baz", "bazinga"),
    });
    cookieContainer.Add(baseAddress, new Cookie("CookieName", "cookie_value"));
    var result = client.PostAsync("/test", content).Result;
    result.EnsureSuccessStatusCode();
}

しかし、マイクロソフトからの最も目に見える例は呼び出しません Dispose()、明示的または暗黙的に。例えば:

発表のコメント、誰かがマイクロソフトの従業員に尋ねました:

サンプルを確認したところ、HttpClientインスタンスで破棄アクションを実行していないことがわかりました。私はアプリのusingステートメントでHttpClientのすべてのインスタンスを使用しましたが、HttpClientがIDisposableインターフェイスを実装しているため、これは正しい方法だと思いました。私は正しい道を進んでいますか?

彼の答えは:

一般にそれは正しいですが、「使用」と非同期には注意が必要ですが、.Net 4では実際には混合されないため、.Net 4.5では「使用」ステートメント内で「待機」を使用できます。

ところで、同じHttpClientは何度でも再利用できるため、通常は常に作成/破棄することはありません。

2番目の段落は、HttpClientインスタンスを何回使用できるかという問題ではなく、不要になった後に破棄する必要があるかどうかについては、この質問には不要です。

(更新:実際には、下の@DPedenで提供されているように、2番目の段落が回答の鍵となります。)

だから私の質問は:

  1. 現在の実装(.NET Framework 4.5)では、HttpClientおよびHttpClientHandlerインスタンスでDispose()を呼び出す必要がありますか?明確化:「必要」とは、リソースの漏洩やデータの破損のリスクなど、処分しないことによる悪影響があるかどうかを意味します。

  2. 彼らがIDisposableを実装しているので、それが必要でない場合でも、とにかく「良い習慣」でしょうか?

  3. 必要な(または推奨される)場合、上記のこのコードはそれを安全に実装していますか(.NET Framework 4.5の場合)?

  4. これらのクラスがDispose()を呼び出す必要がない場合、なぜそれらがIDisposableとして実装されたのですか?

  5. 彼らが必要とする場合、またはそれが推奨される実践である場合、Microsoftの例は誤解を招く、または安全ではないのですか?


2
@Damien_The_Unbeliever、あなたのフィードバックをありがとう。質問を明確にする方法について何か提案はありますか?リソースの漏洩やデータの破損など、リソースを破棄しないことに一般的に関連する問題につながる可能性があるかどうかを知りたいです。
フェルナンドコレイア2013年

9
@Damien_The_Unbeliever:真実ではありません。特に、ストリームライターは正しい動作をするように破棄する必要があります。
Stephen Cleary 2013年

1
@StephenCleary-あなたは何を考えていますか?確かに、Flush書き込みごとに1回呼び出すことができます。また、必要以上に基礎となるリソースを保持し続けるという不便さを除けば、「正しい動作」に必要な何が発生しないのでしょうか。
Damien_The_Unbeliever 2013年

1
これは明らかな誤りです。「原則として、IDisposableオブジェクトを使用するときは、usingステートメントで宣言してインスタンス化する必要があります」。IDisposableを実装するクラスのドキュメントを読んでから、それを使用する必要があるかどうかを判断しました。IDisposableを実装するライブラリの作成者は管理されていないリソースを解放する必要があるため、既存のインスタンスを再利用するのではなく、作成されたコンシューマーが毎回インスタンスを破棄すると、私は恐ろしいでしょう。それは...最終的には、インスタンスを処分していないと言うことではない
markmnl

1
ドキュメントを更新するためにマイクロソフトにPRを提出しました:github.com/dotnet/docs/pull/2470
markmnl

回答:


258

一般的なコンセンサスは、HttpClientを破棄する必要がない(すべきでない)ことです。

それが機能する方法に深く関わっている多くの人々はこれを述べました。

参考のために、Darrel Millerのブログ投稿と関連するSO投稿:HttpClientクロールがメモリリーク引き起こす

また、内部で何が行われているのか、特にここで引用されている「ライフサイクル」セクションのコンテキストについては、ASP.NET使用したEvolvable Web APIの設計のHttpClientの章を読むことを強くお勧めします。

HttpClientはIDisposableインターフェイスを間接的に実装しますが、HttpClientの標準的な使用法は、すべての要求の後でそれを破棄することではありません。HttpClientオブジェクトは、アプリケーションがHTTPリクエストを行う必要がある限り存続することを目的としています。オブジェクトを複数のリクエストにまたがって存在させると、DefaultRequestHeadersを設定する場所が有効になり、HttpWebRequestで必要だったように、すべてのリクエストでCredentialCacheやCookieContainerなどを再指定する必要がなくなります。

または、DotPeekを開きます。


63
あなたの答えを明確にするために、「後で再利用するためにインスタンスを保持している場合、HttpClientを破棄する必要はない」と言うのは正しいでしょうか?たとえば、メソッドが繰り返し呼び出されて新しいHttpClientインスタンスを作成する場合(ほとんどの場合は推奨パターンではありません)、このメソッドがインスタンスを破棄しない(再利用されない)と言っても正しいでしょうか?何千もの処分されていないインスタンスにつながる可能性があります。つまり、インスタンスを再利用してみる必要がありますが、再利用しない場合は、(接続を解放するために)それらを破棄する方がよいということですか。
Fernando Correia 2013

8
当然のことながら苛立たしいことですが、正解は場合によって異なります。ほとんどの場合(私が言ったことはありません)で機能する一般的なアドバイスを提供する必要がある場合は、IoCコンテナーを使用して、HttpClientのインスタンスをシングルトンとして登録することをお勧めします。その場合、インスタンスの存続期間は、コンテナーの存続期間のスコープになります。これは、アプリケーションレベルまたはWebアプリケーションのリクエストごとにスコープを設定できます。
David Peden 2013

25
@FernandoCorreiaはい。何らかの理由でHttpClientインスタンスを繰り返し作成および破棄する場合は、はい、破棄する必要があります。私は、IDisposableインターフェイスを無視することを提案しているのではなく、人々にインスタンスの再利用を奨励しようとしているだけです。
Darrel Miller

20
この回答をさらに信用するために、本日HttpClientチームと話しました。彼らは、HttpClientがリクエストごとに使用するように設計されていないことを確認しました。HttpClientのインスタンスは、クライアントアプリケーションが特定のホストとの対話を継続している間、存続している必要があります。
Darrel Miller

19
@DavidPeden HttpClientをシングルトンとして登録することは、可変であるため、危険だと思われます。たとえば、Timeoutプロパティに割り当てられている全員が互いに踏みつけていませんか?
Jon-Eric

47

現在の回答は少しわかりにくく、誤解を招くものであり、重要なDNSの影響がいくつか欠けています。私は物事が明確に立っている場所を要約してみます。

  1. 一般的に言って、ほとんどのIDisposableオブジェクト、特に名前付き/共有OSリソース所有しているオブジェクト使い終わったときに理想的に破棄する必要がありますダレルミラーとしてので、例外ではありませんHttpClient指摘したように、それはキャンセルトークンを割り当て、リクエスト/レスポンスボディはアンマネージストリームになる可能性がある。
  2. ただし、HttpClientベストプラクティスでは、1つのインスタンスを作成し、それを可能な限り再利用する必要があります(マルチスレッドシナリオでは、そのスレッドセーフメンバーを使用します)。したがって、ほとんどのシナリオでは、常に必要になるという理由だけでそれを処分することはありません
  3. 同じHttpClientを「永久に」再利用する場合の問題は、DNSの変更に関係なく、基になるHTTP接続が元のDNS解決IPに対して開いたままになる可能性があることです。これは、ブルー/グリーン展開やDNSベースのフェイルオーバーなどのシナリオで問題になる可能性があります。この問題に対処するにはさまざまな方法がありますがConnection:close、DNSの変更後にサーバーがヘッダーを送信する最も信頼性の高い方法です。別の可能性としてはHttpClient、定期的に、またはDNSの変更について学習する何らかのメカニズムを介して、クライアント側でをリサイクルすることが含まれます。詳細については、https://github.com/dotnet/corefx/issues/11224を参照してください(リンクされたブログ投稿で提案されているコードを盲目的に使用する前に、注意深く読むことをお勧めします)。

インスタンスでプロキシを切り替えることができないため、常に
破棄

なんらかの理由でHttpClientを破棄する必要がある場合は、HttpClientの破棄に起因する問題の原因が実際に破棄されるため、HttpMessageHandlerの静的インスタンスを保持する必要があります。HttpClientにはコンストラクターオーバーロードがあり、指定したハンドラーを破棄しないように指定できます。この場合、HttpMessageHandlerを他のHttpClientインスタンスで再利用できます。
トム・リント

HttpClientを保持する必要がありますが、System.Net.ServicePointManager.DnsRefreshTimeout = 3000;のようなものを使用できます。これは、たとえば、いつでもwifiと4Gを切り替えるモバイルデバイスを使用している場合に便利です。
JohanFranzén19年

18

私の理解では、Dispose()(特定の接続のように)後で必要になるリソースをロックする場合にのみ、呼び出しが必要です。一般的に使用する必要がないという理由だけで、必要なくなった場合でも、使用していないリソースを解放することを常にお勧めします。(しゃれが意図した)あなたが使用していないリソース上に保持します。

マイクロソフトの例は必ずしも正しくありません。使用されたすべてのリソースは、アプリケーションが終了すると解放されます。そして、その例の場合、それHttpClientは使用が終了した直後に起こります。同様の場合、明示的に呼び出すDispose()はやや不必要です。

ただし、一般に、クラスがを実装する場合、完全に準備ができてできるようになったらすぐに、そのインスタンスをIDisposable理解する必要があることを理解していますDispose()。これは、HttpClientリソースまたは接続がオン/オープンされているかどうかについて明示的に文書化されていない場合などに特に当てはまると思います。接続が再び[間もなく]再利用される場合は、中止する必要がありますDipose()必要があります。その場合、「完全に準備ができている」わけではありません。

参照: IDisposable.DisposeメソッドおよびDisposeを呼び出すタイミング


7
誰かがあなたの家にバナナを持ってきて、それを食べて、皮をむいて立っているようなものです。彼らは皮をどうするべきですか?...彼らがそれを使ってドアから出て行く途中の場合、彼らを行かせてください。彼らが周りにくっついている場合は、それをゴミに捨てて、場所を悪臭がしないようにします。
svidgen 2013年

この答えを明確にするために、「プログラムを使用した直後にプログラムが終了するかどうかを破棄する必要はない」と言っていますか?そして、プログラムがしばらく他のことをすることが期待される場合は、処分する必要がありますか?
フェルナンドコレイア2013年

@FernandoCorreiaはい、私が何かを忘れない限り、それは安全な原則だと思います。ただし、それぞれの場合に考慮してください。たとえば、接続を使用している場合、Dispose()途中で接続を解除したくはなく、既存の接続が再利用可能であれば数秒後に再接続する必要があります。同様に、Dispose()1分か2秒で再構築しなければならない可能性のあるイメージやその他の構造を不必要に使いたくありません。
svidgen 2013年

わかります。しかし、この質問に関するHttpClientとHttpClientHandlerの特定のケースでは、HTTP接続などのリソースを開いたままにしていますか?それが起こっている場合、それらを使用する私のパターンを再考する必要があるかもしれません。
Fernando Correia 2013年

1
@DPedenあなたの答えは私のものとまったく矛盾していません。注意してください、私は言った、あなたが完全に準備ができて、できるようになったらすぐに、そのインスタンスのDispose()をすべきです。インスタンスを再度使用する予定がある場合は、準備ができていません。
svidgen 2013年

9

Dispose()は以下のコードを呼び出し、HttpClientインスタンスによって開かれた接続を閉じます。このコードは、dotPeekを使用して逆コンパイルすることで作成されました。

HttpClientHandler.cs-破棄

ServicePointManager.CloseConnectionGroups(this.connectionGroupName);

disposeを呼び出さない場合、タイマーによって実行されるServicePointManager.MaxServicePointIdleTimeがhttp接続を閉じます。デフォルトは100秒です。

ServicePointManager.cs

internal static readonly TimerThread.Callback s_IdleServicePointTimeoutDelegate = new TimerThread.Callback(ServicePointManager.IdleServicePointTimeoutCallback);
private static volatile TimerThread.Queue s_ServicePointIdlingQueue = TimerThread.GetOrCreateQueue(100000);

private static void IdleServicePointTimeoutCallback(TimerThread.Timer timer, int timeNoticed, object context)
{
  ServicePoint servicePoint = (ServicePoint) context;
  if (Logging.On)
    Logging.PrintInfo(Logging.Web, SR.GetString("net_log_closed_idle", (object) "ServicePoint", (object) servicePoint.GetHashCode()));
  lock (ServicePointManager.s_ServicePointTable)
    ServicePointManager.s_ServicePointTable.Remove((object) servicePoint.LookupString);
  servicePoint.ReleaseAllConnectionGroups();
}

アイドル時間を無限に設定していない場合は、disposeを呼び出さずにアイドル接続タイマーを開始して接続を閉じても安全であるように見えますが、usingステートメントでdisposeを呼び出す方が適切な場合は、 HttpClientインスタンスの処理が完了し、リソースをより早く解放できます。


1
githubでコードを確認することもできます。github.com/dotnet/corefx/blob/master/src/System.Net.Http/src/...
TamusJRoyce

8

短い答え:いいえ、現在受け入れられている答えのステートメントは正確ではありません:「一般的なコンセンサスは、HttpClientを処分する必要がない(すべきではない)ことです」。

長い答え:次のステートメントの両方が真であり、同時に達成可能です。

  1. 「HttpClientは一度インスタンス化され、アプリケーションの存続期間を通して再利用されることを意図しています」と公式ドキュメントから引用されています
  2. IDisposableオブジェクトが配置されるようにお勧めします/想定しています。

そして、それらは必ずしも互いに対立しません。それは、コードをどのように整理して再利用し、HttpClient適切に破棄するかという問題です。

私の別の答えから引用されたさらに長い答え

一部のブログ投稿で、HttpClientIDisposableインターフェイスがどのように使用する傾向があるか非難する人々を見るのは偶然ではありませんusing (var client = new HttpClient()) {...}パターンソケットハンドラーの問題が発生する原因となっている。

私はそれが暗黙の(誤解)概念に帰着すると信じています: 「IDisposableオブジェクトは短命であることが期待されています」

ただし、このスタイルでコードを書くと、短命のように見えますが、

using (var foo = new SomeDisposableObject())
{
    ...
}

IDisposable公式ドキュメントに は決して言及されていませんIDisposableオブジェクトが短命である必要がある。定義により、IDisposableは、管理されていないリソースを解放できるメカニズムにすぎません。これ以上何もない。その意味では、最終的に処分をトリガーすることが期待されますが、短期間でトリガーする必要はありません。

したがって、実際のオブジェクトのライフサイクル要件に基づいて、破棄をトリガーするタイミングを適切に選択するのはあなたの仕事です。IDisposableを長期間にわたって使用するのを妨げるものは何もありません。

using System;
namespace HelloWorld
{
    class Hello
    {
        static void Main()
        {
            Console.WriteLine("Hello World!");

            using (var client = new HttpClient())
            {
                for (...) { ... }  // A really long loop

                // Or you may even somehow start a daemon here

            }

            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
}

この新しい理解により、今度はそのブログ投稿を再訪ます。「修正」がHttpClient一度初期化されるが、決して破棄されないことがはっきりとわかります。そのため、netstat出力から、接続がESTABLISHED状態のままであることがわかります。正しく閉じられていません。閉じられた場合、その状態は代わりにTIME_WAITになります。実際には、プログラム全体が終了した後に開いている接続を1つだけリークすることは大した問題ではなく、ブログの投稿者は修正後もパフォーマンスが向上しています。しかし、それでも、IDisposableを非難し、それを破棄しないことを選択することは概念的には正しくありません。


この説明をありがとうございます。それは明らかにコンセンサスに向かっていくつかの光を当てています。あなたの意見から、いつ呼び出すのが適切だと思いますHttpClient.Disposeか?
Jeson Martajaya

@JesonMartajaya、アプリケーションがhttpClientインスタンスを使用する必要がなくなったときに破棄します。このような提案は曖昧に聞こえるかもしれませんが、実際には、HttpClient client変数のライフサイクルと完全に一致する可能性があります。あなたもまだ使うことができるかもしれませusing (...) {...}ん。たとえば、私の回答内のHello Worldサンプルを参照してください。
RayLuo

7

ここではまだ誰も言及していないようなので、.Net Core 2.1でHttpClientとHttpClientHandlerを管理するための新しい最善の方法は、HttpClientFactory使用することです。です。

それは、前述の問題と落とし穴のほとんどをクリーンで使いやすい方法で解決します。以下からのスティーブ・ゴードンの偉大なブログ記事

.Net Core(2.1.1以降)プロジェクトに次のパッケージを追加します。

Microsoft.AspNetCore.All
Microsoft.Extensions.Http

これをStartup.csに追加します。

services.AddHttpClient();

注入して使用:

[Route("api/[controller]")]
public class ValuesController : Controller
{
    private readonly IHttpClientFactory _httpClientFactory;

    public ValuesController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    [HttpGet]
    public async Task<ActionResult> Get()
    {
        var client = _httpClientFactory.CreateClient();
        var result = await client.GetStringAsync("http://www.google.com");
        return Ok(result);
    }
}

さらに多くの機能については、スティーブのブログの一連の投稿をご覧ください。


4

私の場合、実際にサービスコールを実行するメソッド内にHttpClientを作成していました。何かのようなもの:

public void DoServiceCall() {
  var client = new HttpClient();
  await client.PostAsync();
}

Azureワーカーロールでは、このメソッドを繰り返し呼び出した後(HttpClientを破棄しないで)、最終的に SocketException(接続の試行に失敗して)失敗します。

HttpClientをインスタンス変数にして(クラスレベルで破棄)、問題は解消しました。ですから、そうです、安全な(未処理の非同期呼び出しがない)と仮定して、HttpClientを破棄します。


フィードバックをお寄せいただきありがとうございます。これはやや複雑な問題です。DPedenの回答にリンクされている記事を読むことをお勧めします。つまり、HttpClientインスタンスは、アプリケーションのライフサイクル全体で再利用する必要があります。新しいインスタンスを繰り返し作成する場合は、それらを破棄する必要があります。
フェルナンドコレイア2013年

6
「HttpClientインスタンスは、アプリケーションのライフサイクル全体で再利用する必要があります」これは、多くのアプリケーションで良い考えではありません。HttpClientを使用するWebアプリケーションを考えています。HttpClientは状態(たとえば、使用する要求ヘッダー)を保持するため、1つのWeb要求スレッドが別のスレッドが実行していることを簡単に踏みにじることができます。大規模なWebアプリケーションでは、HttpClientを主要な接続問題の問題としても見ました。疑わしいときは、Disposeと言います。
bytedev

@nashwanヘッダーをクリアして、各リクエストの前に新しいヘッダーを追加することはできませんか?
Mandeep Janjua 2018年

- Microsoftはまた、再利用のHttpClientインスタンスに推奨している docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/...
Mandeep Janjua

@MandeepJanjuaその例は、コンソールアプリケーションとしてのクライアントのようです。私はクライアントであるWebアプリケーションに言及していました。
bytedev '26年

3

一般的な使用方法(応答<2GB)では、HttpResponseMessagesを破棄する必要はありません。

ストリームコンテンツが完全に読み取られていない場合、HttpClientメソッドの戻り値の型は破棄する必要があります。そうしないと、CLRがガベージコレクションされるまでこれらのストリームを閉じることができることをCLRが知る方法はありません。

  • データをバイト[](例:GetByteArrayAsync)または文字列に読み込む場合、すべてのデータが読み込まれるため、破棄する必要はありません。
  • 他のオーバーロードはデフォルトで最大2GBのストリームを読み取るようになります(HttpCompletionOptionはResponseContentRead、HttpClient.MaxResponseContentBufferSizeのデフォルトは2GBです)

HttpCompletionOptionをResponseHeadersReadに設定するか、応答が2GBより大きい場合は、クリーンアップする必要があります。これは、HttpResponseMessageでDisposeを呼び出すか、HttpResonseMessageコンテンツから取得したストリームでDispose / Closeを呼び出すか、コンテンツを完全に読み取ることで実行できます。

HttpClientでDisposeを呼び出すかどうかは、保留中の要求をキャンセルするかどうかによって異なります。


2

HttpClientを破棄したい場合は、リソースプールとして設定すれば破棄できます。そして、アプリケーションの最後に、リソースプールを破棄します。

コード:

// Notice that IDisposable is not implemented here!
public interface HttpClientHandle
{
    HttpRequestHeaders DefaultRequestHeaders { get; }
    Uri BaseAddress { get; set; }
    // ...
    // All the other methods from peeking at HttpClient
}

public class HttpClientHander : HttpClient, HttpClientHandle, IDisposable
{
    public static ConditionalWeakTable<Uri, HttpClientHander> _httpClientsPool;
    public static HashSet<Uri> _uris;

    static HttpClientHander()
    {
        _httpClientsPool = new ConditionalWeakTable<Uri, HttpClientHander>();
        _uris = new HashSet<Uri>();
        SetupGlobalPoolFinalizer();
    }

    private DateTime _delayFinalization = DateTime.MinValue;
    private bool _isDisposed = false;

    public static HttpClientHandle GetHttpClientHandle(Uri baseUrl)
    {
        HttpClientHander httpClient = _httpClientsPool.GetOrCreateValue(baseUrl);
        _uris.Add(baseUrl);
        httpClient._delayFinalization = DateTime.MinValue;
        httpClient.BaseAddress = baseUrl;

        return httpClient;
    }

    void IDisposable.Dispose()
    {
        _isDisposed = true;
        GC.SuppressFinalize(this);

        base.Dispose();
    }

    ~HttpClientHander()
    {
        if (_delayFinalization == DateTime.MinValue)
            _delayFinalization = DateTime.UtcNow;
        if (DateTime.UtcNow.Subtract(_delayFinalization) < base.Timeout)
            GC.ReRegisterForFinalize(this);
    }

    private static void SetupGlobalPoolFinalizer()
    {
        AppDomain.CurrentDomain.ProcessExit +=
            (sender, eventArgs) => { FinalizeGlobalPool(); };
    }

    private static void FinalizeGlobalPool()
    {
        foreach (var key in _uris)
        {
            HttpClientHander value = null;
            if (_httpClientsPool.TryGetValue(key, out value))
                try { value.Dispose(); } catch { }
        }

        _uris.Clear();
        _httpClientsPool = null;
    }
}

var handler = HttpClientHander.GetHttpClientHandle(new Uri( "base url"))。

  • HttpClientは、インターフェイスとしてDispose()を呼び出すことができません。
  • Dispose()は、ガベージコレクタによって遅延方式で呼び出されます。または、プログラムがデストラクタを使用してオブジェクトをクリーンアップするとき。
  • 弱参照と遅延クリーンアップロジックを使用しているため、頻繁に再利用されている限り使用され続けます。
  • 渡された各ベースURLに新しいHttpClientを割り当てるだけです。Ohad Schneiderが説明する理由は以下のとおりです。ベースURLを変更するときの不正な動作。
  • HttpClientHandleはテストでモッキングを可能にします

完璧です。DisposeGCに登録するメソッドを呼び出すことがわかります。これは上部でより高く評価されるべきです。
ジェソンマルタジャヤ

HttpClientはベースURLごとにリソースプーリングを行うことに注意してください。したがって、リスト内の何千もの異なるWebサイトにアクセスしている場合、個々のサイトをクリーンアップしないとパフォーマンスが低下します。これにより、各ベースURLを破棄する機能が公開されます。ただし、1つのWebサイトのみを使用している場合は、disposeを呼び出すのは学問上の理由にすぎない場合があります。
TamusJRoyce

1

コンストラクターで依存性注入を使用すると、ライフタイムの管理がHttpClient容易になります。ライフタイム管理は、それを必要とするコードの外で行い、後で簡単に変更できるようにします。

現在の私の好みは、ターゲットエンドポイントドメインごとに1回継承する独立したhttpクライアントクラスHttpClient作成し、依存関係注入を使用してそれをシングルトンにすることです。public class ExampleHttpClient : HttpClient { ... }

次に、そのAPIにアクセスする必要があるサービスクラスで、カスタムhttpクライアントのコンストラクター依存関係を取得します。これは寿命の問題を解決し、接続プーリングに関しては利点があります。

https://stackoverflow.com/a/50238944/3140853の関連する回答で動作する例を見ることができます



-2

シングルトンパターンを使用して、HttpClientのインスタンスを作成して常に閉じる必要がないようにする必要があると思います。.Net 4.0を使用している場合は、以下のサンプルコードを使用できます。シングルトンパターンの詳細については、こちらを確認してください

class HttpClientSingletonWrapper : HttpClient
{
    private static readonly Lazy<HttpClientSingletonWrapper> Lazy= new Lazy<HttpClientSingletonWrapper>(()=>new HttpClientSingletonWrapper()); 

    public static HttpClientSingletonWrapper Instance {get { return Lazy.Value; }}

    private HttpClientSingletonWrapper()
    {
    }
}

以下のコードを使用します。

var client = HttpClientSingletonWrapper.Instance;

3
これ(および他の同様のスキーム)を実行するときに注意する点:「インスタンスメンバーはスレッドセーフであるとは限りません。
tne

2
この答えが正しいかどうかは、HttpClientを使用するアプリケーションの種類に完全に依存する必要があります。Webアプリケーションがあり、すべてのWebリクエストが共有するシングルトンのHttpClientを作成すると、多くの接続例外が発生する可能性があります(Webサイトの人気度によって異なります!:-))。(David Faivreの回答を参照)
bytedev
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.