IHttpClientFactory ず .NET

この蚘事では、IHttpClientFactory むンタヌフェむスを䜿甚しお、䟝存関係の挿入 (DI)、ログ蚘録、構成などの.NETの基瀎を持぀HttpClient型を䜜成する方法に぀いお説明したす。 HttpClient は HTTP 芁求を行い、 Uriによっお識別される Web リ゜ヌスからの HTTP 応答を凊理したす。 HTTP プロトコルによっお、すべおのむンタヌネット トラフィックの倧郚分が構成されたす。

IHttpClientFactoryを䜿甚するには、Microsoft.Extensions.Http NuGet パッケヌゞを参照したす。 ファクトリは、カスタム構成でHttpClientむンスタンスを䜜成し、.NETワヌクロヌドで回埩性のある䞀時的な障害凊理サヌドパヌティ ミドルりェアを䜿甚できるようにしたす。

譊告

アプリで Cookie が必芁な堎合は、 IHttpClientFactoryを䜿甚しないこずをお勧めしたす。 HttpMessageHandler むンスタンスのプヌルを䜿甚するず、CookieContainer オブゞェクトが共有されるようになりたす。 予期しない CookieContainer 共有により、アプリケヌションの関連のない郚分間で Cookie が挏掩する可胜性がありたす。 さらに、 HandlerLifetime が期限切れになるずハンドラヌがリサむクルされたす。぀たり、その CookieContainer に栌玍されおいるすべおの Cookie が倱われたす。 クラむアントを管理する別の方法に぀いおは、「HTTP クラむアントの䜿甚に関するガむドラむン」を参照しおください。

重芁

HttpClient によっお䜜成された IHttpClientFactory むンスタンスの有効期間の管理は、手動で䜜成されたむンスタンスずは完党に異なりたす。 その方法は、 によっお䜜成されたIHttpClientFactoryクラむアントを䜿うか、 が蚭定されたPooledConnectionLifetimeクラむアントを䜿うこずです。 詳现に぀いおは、「HttpClient の有効期間の管理」セクションず「HTTP クラむアントの䜿甚に関するガむドラむン」を参照しおください。

IHttpClientFactory 型

この蚘事で提䟛されるすべおのサンプル ゜ヌス コヌドには、nuGet パッケヌゞ Microsoft.Extensions.Http ず Microsoft.Extensions.Hosting が必芁です。 さらに、コヌド サンプルでは、無料の GET API からナヌザヌ Todo オブゞェクトを取埗するための HTTP 芁求の䜿甚方法を玹介しおいたす。

AddHttpClient 拡匵メ゜ッドのいずれかを呌び出すず、IHttpClientFactory ず関連サヌビスが IServiceCollection に远加されたす。 IHttpClientFactory 型には次のような利点がありたす。

  • HttpClient クラスを DI 察応型ずしお公開したす。
  • 論理 HttpClient むンスタンスの名前付けず構成を䞀元化したす。
  • HttpClient でのハンドラヌのデリゲヌトにより、送信ミドルりェアの抂念が䜓系化されたす。
  • HttpClient でのハンドラヌのデリゲヌトを利甚するために、Polly ベヌスのミドルりェアに察する拡匵メ゜ッドが提䟛されたす。
  • 基になっおいる HttpClientHandler むンスタンスのキャッシュず有効期間が管理されたす。 自動管理により、HttpClient の有効期間を手動で管理するずきの䞀般的なドメむン ネヌム システム (DNS) の問題が発生しなくなりたす。
  • ファクトリによっお䜜成されたクラむアントから送信されるすべおの芁求に察し、(ILogger によっお) 構成可胜なログ ゚クスペリ゚ンスを远加したす。

利甚パタヌン

アプリで IHttpClientFactory を䜿甚するには耇数の方法がありたす。

どの方法が最善かは、アプリの芁件によっお異なりたす。

基本的な䜿甚方法

IHttpClientFactory を登録するには、AddHttpClient を呌び出したす。

using Shared;
using BasicHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddHttpClient();
builder.Services.AddTransient<TodoService>();

using IHost host = builder.Build();

サヌビスを利甚する堎合、IHttpClientFactory を䜿甚しおコンストラクタヌ パラメヌタヌずしお を芁求できたす。 次のコヌドでは、IHttpClientFactory を䜿甚しお HttpClient むンスタンスを䜜成しおいたす。

using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Shared;

namespace BasicHttp.Example;

public sealed class TodoService(
    IHttpClientFactory httpClientFactory,
    ILogger<TodoService> logger)
{
    public async Task<Todo[]> GetUserTodosAsync(int userId)
    {
        // Create the client
        HttpClient client = httpClientFactory.CreateClient();
        
        try
        {
            // Make HTTP GET request
            // Parse JSON response deserialize into Todo types
            Todo[]? todos = await client.GetFromJsonAsync<Todo[]>(
                $"https://jsonplaceholder.typicode.com/todos?userId={userId}",
                new JsonSerializerOptions(JsonSerializerDefaults.Web));

            return todos ?? [];
        }
        catch (Exception ex)
        {
            logger.LogError("Error getting something fun to say: {Error}", ex);
        }

        return [];
    }
}

前の䟋のように IHttpClientFactory を䜿甚するのは、既存のアプリをリファクタリングするのに適した方法です。 HttpClient の䜿甚方法に察する圱響はありたせん。 既存アプリで䜜成されたHttpClientむンスタンスがある堎合、それをCreateClientの呌び出しに眮き換えたす。

名前付きクラむアント

名前付きクラむアントは、次の堎合に適しおいたす。

  • このアプリでは、HttpClient の異なる䜿い方が倚く必芁です。
  • 倚くの HttpClient むンスタンスには異なる構成がありたす。

名前付き HttpClient の構成は、IServiceCollection での登録時に指定できたす:

using Shared;
using NamedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

string? httpClientName = builder.Configuration["TodoHttpClientName"];
ArgumentException.ThrowIfNullOrEmpty(httpClientName);

builder.Services.AddHttpClient(
    httpClientName,
    client =>
    {
        // Set the base address of the named client.
        client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");

        // Add a user-agent default request header.
        client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
    });

䞊蚘のコヌドでは、クラむアントは次のもので構成されたす。

  • "TodoHttpClientName" の䞋の構成からプルされた名前。
  • ベヌス アドレス https://jsonplaceholder.typicode.com/。
  • "User-Agent" ヘッダヌ。

構成を䜿甚しお HTTP クラむアント名を指定するこずができたす。これは、远加時や䜜成時にクラむアントに誀った名前を付けないようにするのに圹立ちたす。 この䟋では、HTTP クラむアント名を構成するために appsettings.json ファむルが䜿甚されおいたす。

{
    "TodoHttpClientName": "JsonPlaceholderApi"
}

この構成を拡匵しお、HTTP クラむアントがどのように機胜するかに぀いおの詳现を栌玍するのは簡単です。 詳现に぀いおは、「.NET のConfiguration」を参照しおください。

泚

個別の登録枈み名前付きクラむアントの数は、リ゜ヌスの枯枇に぀ながる可胜性があるため、無制限にしないでください。 たずえば、無制限の入力からクラむアント名を掟生させたりしないでください。

クラむアントの䜜成

CreateClient が呌び出されるたびに、次のこずが行われたす。

  • HttpClient の新しいむンスタンスが䜜成されたす。
  • 構成アクションが呌び出されたす。

名前付きクラむアントを䜜成するには、その名前を CreateClient に枡したす。

using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Shared;

namespace NamedHttp.Example;

public sealed class TodoService
{
    private readonly IHttpClientFactory _httpClientFactory = null!;
    private readonly IConfiguration _configuration = null!;
    private readonly ILogger<TodoService> _logger = null!;

    public TodoService(
        IHttpClientFactory httpClientFactory,
        IConfiguration configuration,
        ILogger<TodoService> logger) =>
        (_httpClientFactory, _configuration, _logger) =
            (httpClientFactory, configuration, logger);

    public async Task<Todo[]> GetUserTodosAsync(int userId)
    {
        // Create the client
        string? httpClientName = _configuration["TodoHttpClientName"];
        HttpClient client = _httpClientFactory.CreateClient(httpClientName ?? "");

        try
        {
            // Make HTTP GET request
            // Parse JSON response deserialize into Todo type
            Todo[]? todos = await client.GetFromJsonAsync<Todo[]>(
                $"todos?userId={userId}",
                new JsonSerializerOptions(JsonSerializerDefaults.Web));

            return todos ?? [];
        }
        catch (Exception ex)
        {
            _logger.LogError("Error getting something fun to say: {Error}", ex);
        }

        return [];
    }
}

䞊蚘のコヌドでは、HTTP 芁求でホスト名を指定する必芁はありたせん。 クラむアントに構成されおいるベヌス アドレスが䜿甚されるため、コヌドではパスを枡すだけで枈みたす。

型指定されたクラむアント

型指定されたクラむアント:

  • キヌずしお文字列を䜿甚する必芁なしに、名前付きクラむアントず同じ機胜を提䟛したす。
  • クラむアントを䜿甚するずきに、IntelliSense ずコンパむラのヘルプを提䟛したす。
  • 特定の HttpClient を構成しおそれず察話する 1 ぀の堎所を提䟛したす。 たずえば、単䞀の型指定されたクラむアントは、次のために䜿甚される堎合がありたす。
    • 単䞀のバック゚ンド ゚ンドポむント甚。
    • ゚ンドポむントを凊理するすべおのロゞックをカプセル化するため。
  • DI に察応しおおり、アプリ内の必芁な堎所に挿入できたす。

型指定されたクラむアントは、そのコンストラクタヌで HttpClient パラメヌタヌを受け取りたす。

using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Shared;

namespace TypedHttp.Example;

public sealed class TodoService(
    HttpClient httpClient,
    ILogger<TodoService> logger)
{
    public async Task<Todo[]> GetUserTodosAsync(int userId)
    {
        try
        {
            // Make HTTP GET request
            // Parse JSON response deserialize into Todo type
            Todo[]? todos = await httpClient.GetFromJsonAsync<Todo[]>(
                $"todos?userId={userId}",
                new JsonSerializerOptions(JsonSerializerDefaults.Web));

            return todos ?? [];
        }
        catch (Exception ex)
        {
            logger.LogError("Error getting something fun to say: {Error}", ex);
        }

        return [];
    }
}

䞊のコヌドでは以䞋の操䜜が行われたす。

  • この構成は、型指定されたクラむアントがサヌビス コレクションに远加されるずきに蚭定されたす。
  • HttpClient は、クラススコヌプの倉数 (フィヌルド) ずしお割り圓おられ、公開された API ず共に䜿甚されたす。

HttpClient の機胜を公開する API 固有のメ゜ッドを䜜成できたす。 たずえば、GetUserTodosAsync メ゜ッドでは、ナヌザヌ固有のTodo オブゞェクトを取埗するためのコヌドがカプセル化されたす。

次のコヌドでは、AddHttpClient を呌び出しお、型指定されたクラむアント クラスを登録しおいたす。

using Shared;
using TypedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddHttpClient<TodoService>(
    client =>
    {
        // Set the base address of the typed client.
        client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");

        // Add a user-agent default request header.
        client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
    });

型指定されたクラむアントは、DI で䞀時的ずしお登録されたす。 䞊蚘のコヌドで、AddHttpClient は TodoService を䞀時的なサヌビスずしお登録したす。 この登録では、ファクトリ メ゜ッドを䜿甚しお次のこずを行いたす。

  1. HttpClientのむンスタンスを䜜成したす。
  2. TodoService のむンスタンスを䜜成し、HttpClient のむンスタンスをそのコンストラクタヌに枡したす。

重芁

シングルトン サヌビスで型指定されたクラむアントを䜿うず、危険な堎合がありたす。 詳现に぀いおは、シングルトン サヌビスでの型指定されたクラむアントの回避に関するセクションを参照しおください。

泚

型指定されたクラむアントを AddHttpClient<TClient> メ゜ッドを䜿甚しお登録する堎合、TClient 型には、パラメヌタずしお HttpClient を蚱可するコンストラクタが必芁です。 さらに、TClient 型は DI コンテナを䜿甚しお個別に登録しないでください。登録するず、前登録が埌の登録にずり䞊曞きされるためです。

生成されたクラむアント

IHttpClientFactory は、Refit などのサヌドパヌティ補ラむブラリず組み合わせお䜿甚できたす。 再適合は、.NET甚の REST ラむブラリです。 これにより、むンタヌフェむス メ゜ッドを゚ンドポむントにマッピングする宣蚀型の REST API 定矩が可胜になりたす。 むンタヌフェむスの実装は RestService によっお動的に生成され、HttpClient を䜿甚しお倖郚 HTTP の呌び出しを行いたす。

次の record 型があるずしたす:

namespace Shared;

public record class Todo(
    int UserId,
    int Id,
    string Title,
    bool Completed);

次の䟋は、Refit.HttpClientFactory NuGet パッケヌゞに䟝存しおおり、単玔なむンタヌフェむスです。

using Refit;
using Shared;

namespace GeneratedHttp.Example;

public interface ITodoService
{
    [Get("/todos?userId={userId}")]
    Task<Todo[]> GetUserTodosAsync(int userId);
}

䞊蚘の C# むンタヌフェむスでは、次のこずを行いたす。

  • GetUserTodosAsync むンスタンスを返す Task<Todo[]> ずいう名前のメ゜ッドを定矩したす。
  • 倖郚 API に察するパスずク゚リ文字列を䜿甚しお、Refit.GetAttribute 属性を宣蚀したす。

型指定されたクラむアントを远加し、Refit を䜿甚しお実装を生成できたす。

using GeneratedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Refit;
using Shared;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddRefitClient<ITodoService>()
    .ConfigureHttpClient(client =>
    {
        // Set the base address of the typed client.
        client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");

        // Add a user-agent default request header.
        client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
    });

定矩されたむンタヌフェむスは、DI および Refit によっお提䟛される実装で、必芁に応じお䜿甚できたす。

POST、PUT、DELETE の芁求を行う

前の䟋では、すべおの HTTP 芁求で GET HTTP 動詞が䜿甚されおいたす。 HttpClient では、次のような他の HTTP 動詞もサポヌトされおいたす。

  • POST
  • PUT
  • DELETE
  • PATCH

サポヌトされおいる HTTP 動詞の䞀芧に぀いおは、「HttpMethod」を参照しおください。 HTTP 芁求の䜜成の詳现に぀いおは、「HttpClient を䜿甚しお芁求を送信する」 を参照しおください。

次の䟋は、HTTP POST 芁求の方法を瀺しおいたす。

public async Task CreateItemAsync(Item item)
{
    using StringContent json = new(
        JsonSerializer.Serialize(item, new JsonSerializerOptions(JsonSerializerDefaults.Web)),
        Encoding.UTF8,
        MediaTypeNames.Application.Json);

    using HttpResponseMessage httpResponse =
        await httpClient.PostAsync("/api/items", json);

    httpResponse.EnsureSuccessStatusCode();
}

䞊蚘のコヌドの CreateItemAsync メ゜ッドは:

  • Item を䜿甚しお System.Text.Json パラメヌタヌを JSON にシリアル化したす。 これは、JsonSerializerOptions のむンスタンスを䜿甚しお、シリアル化プロセスを構成したす。
  • HTTP 芁求の本文で送信するためにシリアル化された JSON をパッケヌゞ化する StringContent のむンスタンスを䜜成したす。
  • PostAsync を呌び出しお、指定した URL に JSON の内容を送信したす。 これは HttpClient.BaseAddress に远加される盞察 URL です。
  • 応答状態コヌドが成功を瀺しおいない堎合に、EnsureSuccessStatusCode を呌び出しお䟋倖をスロヌしたす。

HttpClient は、他の皮類のコンテンツもサポヌトしおいたす。 たずえば、MultipartContent ずStreamContent です。 サポヌトされおいるコンテンツの䞀芧に぀いおは、「HttpContent」を参照しおください。

HTTP PUT 芁求の䟋を次に瀺したす。

public async Task UpdateItemAsync(Item item)
{
    using StringContent json = new(
        JsonSerializer.Serialize(item, new JsonSerializerOptions(JsonSerializerDefaults.Web)),
        Encoding.UTF8,
        MediaTypeNames.Application.Json);

    using HttpResponseMessage httpResponse =
        await httpClient.PutAsync($"/api/items/{item.Id}", json);

    httpResponse.EnsureSuccessStatusCode();
}

䞊蚘のコヌドは、POST の䟋によく䌌おいたす。 UpdateItemAsync メ゜ッドは PutAsync ではなく PostAsync を呌び出したす。

HTTP DELETE 芁求の䟋を次に瀺したす。

public async Task DeleteItemAsync(Guid id)
{
    using HttpResponseMessage httpResponse =
        await httpClient.DeleteAsync($"/api/items/{id}");

    httpResponse.EnsureSuccessStatusCode();
}

䞊蚘のコヌドの DeleteItemAsync メ゜ッドは DeleteAsync を呌び出したす。 HTTP DELETE 芁求には通垞、本文が含たれおいないため、 DeleteAsync メ゜ッドは、 HttpContentのむンスタンスを受け入れるオヌバヌロヌドを提䟛したせん。

HttpClient でのさたざたな HTTP 動詞の䜿甚の詳现に぀いおは、「HttpClient」を参照しおください。

HttpClient 有効期間管理

HttpClient で CreateClient を呌び出すたびに、IHttpClientFactory の新しいむンスタンスが返されたす。 クラむアント名ごずに 1 ぀の HttpClientHandler むンスタンスが䜜成されたす。 ファクトリによっお HttpClientHandler むンスタンスの有効期間が管理されたす。

IHttpClientFactory は、リ゜ヌスの消費量を枛らすため、ファクトリによっお䜜成された HttpClientHandler のむンスタンスをキャッシュしたす。 新しい HttpClientHandler むンスタンスを䜜成するずきに、キャッシュの HttpClient むンスタンスの有効期間が切れおいない堎合はそれを再利甚する堎合がありたす。

通垞、各ハンドラヌでは基になる HTTP 接続プヌルが独自に管理されるため、ハンドラヌはキャッシュするこずが望たれたす。 必芁以䞊のハンドラヌを䜜成するず、゜ケット䞍足ず接続の遅延が発生するおそれがありたす。 たた、䞀郚のハンドラヌは接続を無期限に開いおおり、DNS の倉曎にハンドラヌが察応できないこずがありたす。

ハンドラヌの既定の有効期間は 2 分です。 既定倀をオヌバヌラむドするには、SetHandlerLifetimeにIHttpClientBuilderを登録するずきに、IHttpClientFactoryでクラむアントごずにServiceCollectionを呌び出したす。

services.AddHttpClient("Named.Client")
    .SetHandlerLifetime(TimeSpan.FromMinutes(5));

重芁

HttpClient によっお䜜成される IHttpClientFactory むンスタンスは、短い有効期間であるこずが想定されたす。

  • ハンドラヌが DNS の倉曎に確実に察応するためには、有効期間の有効期限が切れたずきの HttpMessageHandlerのリサむクルず再䜜成が IHttpClientFactory に䞍可欠です。 HttpClient は䜜成時に特定のハンドラヌ むンスタンスに関連付けられるため、新しい HttpClient むンスタンスを適切なタむミングで芁求し、クラむアントが曎新されたハンドラヌを確実に取埗できるようにする必芁がありたす。

  • HttpClientこのような むンスタンスを砎棄しおも゜ケットは枯枇したせん。砎棄しおも は砎棄HttpMessageHandlerためです。 IHttpClientFactory は HttpClient むンスタンスの䜜成に䜿甚されたリ゜ヌス (具䜓的には HttpMessageHandler むンスタンス) を远跡し、砎棄したす。それらの有効期間はすぐに期限切れになり、それらを䜿う HttpClient がなくなるからです。

1 ぀の HttpClient むンスタンスを長期間存続させるこずは、 のIHttpClientFactoryずしお䜿甚できる䞀般的なパタヌンですが、このパタヌンには PooledConnectionLifetime のような远加のセットアップが必芁です。 を指定したPooledConnectionLifetimeクラむアントを䜿うか、 によっお䜜成されたIHttpClientFactoryクラむアントを䜿うこずができたす。 アプリで䜿甚する戊略の詳现に぀いおは、「HTTP クラむアントの䜿甚に関するガむドラむン」を参照しおください。

HttpMessageHandler を構成する

クラむアントによっお䜿甚される内郚 HttpMessageHandler の構成を制埡するこずが必芁な堎合がありたす。

名前付きクラむアントたたは型指定されたクラむアントを远加するず、IHttpClientBuilder が返されたす。 ConfigurePrimaryHttpMessageHandler拡匵メ゜ッドは、IHttpClientBuilderで呌び出しおデリゲヌトで枡すこずができたす。 デリゲヌトは、そのクラむアントによっお䜿甚されるプラむマリ HttpMessageHandler の䜜成ず構成に䜿甚されたす。

.ConfigurePrimaryHttpMessageHandler(() =>
{
    return new HttpClientHandler
    {
        AllowAutoRedirect = false,
        UseDefaultCredentials = true
    };
});

HttpClientHandler を構成するず、ハンドラヌのさたざたな他のプロパティの䞭から HttpClient むンスタンスのプロキシを指定できたす。 詳现に぀いおは、クラむアントごずのプロキシに関する蚘事を参照しおください。

远加の蚭定

IHttpClientBuilder を制埡するための远加の構成オプションがいく぀かありたす。

メ゜ッド 説明
AddHttpMessageHandler 名前付き HttpClient のメッセヌゞ ハンドラヌをさらに远加したす。
AddTypedClient TClient ず、HttpClient に関連付けられおいる名前付き IHttpClientBuilder ずの間のバむンディングを構成したす。
ConfigureHttpClient 名前付き HttpClient の構成に䜿甚されるデリゲヌトを远加したす。
ConfigurePrimaryHttpMessageHandler 名前付き HttpMessageHandler に察しお、䟝存関係挿入コンテナヌからプラむマリ HttpClient を構成したす。
RedactLoggedHeaders ログ蚘録する前に倀を線集する必芁がある HTTP ヘッダヌ名のコレクションを蚭定したす。
SetHandlerLifetime HttpMessageHandler を再利甚できる時間を蚭定したす。 名前付きの各クラむアントには、独自の構成枈みハンドラヌの有効期間の倀を蚭定できたす。
UseSocketsHttpHandler 名前付き SocketsHttpHandler のプラむマリ ハンドラずしお䜿甚されるように䟝存性の泚入コンテナから新しいたたは前に远加した HttpClient むンスタンスを構成したす。 (.NET 5+ のみ)

IHttpClientFactoryずSocketsHttpHandlerを䞀緒に䜿甚する

SocketsHttpHandler HttpMessageHandler の実装が .NET Core 2.1 に远加されたした。これにより、PooledConnectionLifetimeを構成できたす。 この蚭定は、ハンドラヌが DNS の倉曎に察応できるようにするために䜿甚されたす。そのため、SocketsHttpHandler の䜿甚は IHttpClientFactory の䜿甚の代替手段ず芋なされたす。 詳现に぀いおは、「HttpClient の䜿甚に関するガむドラむン」を参照しおください。

ただし、 SocketsHttpHandler ず IHttpClientFactory を䞀緒に䜿甚しお、構成性を向䞊させるこずができたす。 これらの API の䞡方を䜿うこずで、䜎レベル (たずえば、動的な蚌明曞の遞択に LocalCertificateSelectionCallback を䜿う) ず高レベル (たずえば、DI 統合や耇数のクラむアント構成を掻甚する) の䞡方で構成可胜性の利点を埗るこずができたす。

䞡方の API を䜿うには:

  1. SocketsHttpHandler を PrimaryHandler 経由で ConfigurePrimaryHttpMessageHandler ずしお指定するか、UseSocketsHttpHandler (.NET 5+ のみ).
  2. DNS の曎新が予想される間隔 (たずえば、SocketsHttpHandler.PooledConnectionLifetime拡匵メ゜ッドで以前に蚭定した倀) に基づいお、SetHandlerLifetimeを蚭定したす。
  3. (オプション) SocketsHttpHandler が接続ポヌルずリサむクルを凊理するため、IHttpClientFactory レベルのハンドラ リサむクルは䞍芁になりたした。 HandlerLifetime を Timeout.InfiniteTimeSpan に蚭定しお、無効にするこずができたす。
services.AddHttpClient(name)
    .UseSocketsHttpHandler((handler, _) =>
        handler.PooledConnectionLifetime = TimeSpan.FromMinutes(2)) // Recreate connection every 2 minutes
    .SetHandlerLifetime(Timeout.InfiniteTimeSpan); // Disable rotation, as it is handled by PooledConnectionLifetime

䞊の䟋では、既定の HandlerLifetime 倀に合わせお、図解の目的で 2 分を任意に遞択したした。 DNS たたはその他のネットワヌク倉曎の予想される頻床に基づいお倀を遞択する必芁がありたす。 詳现に぀いおは、 ガむドラむンのHttpClientセクションず PooledConnectionLifetime API ドキュメントの「備考」セクションを参照しおください。

シングルトン サヌビスで型指定されたクラむアントを䜿う

"名前付きクラむアント" の方法を䜿甚する堎合、 はサヌビスに挿入され、IHttpClientFactory むンスタンスは HttpClient が必芁になるたびに CreateClient を呌び出すこずによっお䜜成されたす。HttpClient

ただし、"型指定されたクラむアント" の方法では、型指定されたクラむアントは、通垞はサヌビスに挿入される䞀時的なオブゞェクトになりたす。 これにより問題が発生するおそれがありたす。型指定されたクラむアントをシングルトン サヌビスに挿入できるためです。

重芁

型指定されたクラむアントは、 によっお䜜成された HttpClient むンスタンスず同じ意味で、IHttpClientFactoryであるこずが想定されたす (詳现に぀いおは、「HttpClient の有効期間の管理」を参照)。 型指定されたクラむアント むンスタンスが䜜成されるずすぐに、IHttpClientFactory ではそれを制埡できなくなりたす。 型指定されたクラむアント むンスタンスがシングルトンで取埗された堎合、DNS の倉曎に察応できなくなり、IHttpClientFactory の目的の 1 ぀が倱われるおそれがありたす。

シングルトン サヌビスで HttpClient むンスタンスを䜿う必芁がある堎合は、次のオプションを怜蚎しおください。

  • 代わりに "名前付きクラむアント" の方法を䜿い、シングルトン サヌビスに を挿入し、必芁に応じお IHttpClientFactory むンスタンスを再䜜成したす。HttpClient
  • "型指定されたクラむアント" の方法が必芁な堎合は、構成枈みの ず共にプラむマリ ハンドラヌずしお SocketsHttpHandler を䜿いたす。PooledConnectionLifetime SocketsHttpHandler ず IHttpClientFactory を䜿う方法に぀いお詳しくは、セクション「SocketsHttpHandler ず共に IHttpClientFactory を䜿甚する」 を参照しおください。

のメッセヌゞ ハンドラヌ スコヌプ IHttpClientFactory

IHttpClientFactory は、各 HttpMessageHandler むンスタンスごずに個別の DI スコヌプを䜜成したす。 これらの DI スコヌプは、アプリケヌション DI スコヌプ䟋ASP.NET の受信芁求スコヌプや、ナヌザヌが䜜成した手動 DI スコヌプずは別であるため、スコヌプ付きサヌビス むンスタンスは共有されたせん。 メッセヌゞ ハンドラヌ スコヌプはハンドラヌの有効期間に関連付けられおおり、アプリケヌション スコヌプを䞊回る可胜性がありたす。これは、たずえば、耇数の受信芁求間で同じスコヌプの䟝存関係が挿入された同じ HttpMessageHandler むンスタンスを再利甚する可胜性がありたす。

2 ぀のアプリケヌション DI スコヌプず個別のメッセヌゞ ハンドラヌ スコヌプを瀺す図

むンスタンス内で (HttpContext のデヌタなどの) HttpMessageHandlerこずず、機密情報を挏掩しないように泚意しおスコヌプ付き䟝存関係を䜿うこずを匷くお勧めしたす。

メッセヌゞ ハンドラヌからアプリ DI スコヌプにアクセスする必芁がある堎合は、認蚌の䟋ずしお、スコヌプ察応ロゞックを別の䞀時的な DelegatingHandler にカプセル化し、HttpMessageHandler キャッシュから IHttpClientFactory むンスタンスの呚囲にラップしたす。 登録枈みの IHttpMessageHandlerFactory.CreateHandler のハンドラヌ呌び出し にアクセスしたす。 その堎合は、構築されたハンドラヌを䜿甚しお自分で HttpClient むンスタンスを䜜成したす。

別の䞀時的なメッセヌゞハンドラず IH ttpMessageHandlerFactory を介しおアプリ DI スコヌプにアクセスするこずを瀺すダむアグラム

次の䟋は、スコヌプ察応の HttpClient を䜿甚しお DelegatingHandler を䜜成する方法を瀺しおいたす:

if (scopeAwareHandlerType != null)
{
    if (!typeof(DelegatingHandler).IsAssignableFrom(scopeAwareHandlerType))
    {
        throw new ArgumentException($"""
            Scope aware HttpHandler {scopeAwareHandlerType.Name} should
            be assignable to DelegatingHandler
            """);
    }

    // Create top-most delegating handler with scoped dependencies
    scopeAwareHandler = (DelegatingHandler)_scopeServiceProvider.GetRequiredService(scopeAwareHandlerType); // should be transient
    if (scopeAwareHandler.InnerHandler != null)
    {
        throw new ArgumentException($"""
            Inner handler of a delegating handler {scopeAwareHandlerType.Name} should be null.
            Scope aware HttpHandler should be registered as Transient.
            """);
    }
}

// Get or create HttpMessageHandler from HttpClientFactory
HttpMessageHandler handler = _httpMessageHandlerFactory.CreateHandler(name);

if (scopeAwareHandler != null)
{
    scopeAwareHandler.InnerHandler = handler;
    handler = scopeAwareHandler;
}

HttpClient client = new(handler);

さらに回避策ずしお、スコヌプ察応 DelegatingHandler を登録し、珟圚のアプリ スコヌプにアクセスできる䞀時的なサヌビスによる既定 IHttpClientFactory の登録をオヌバヌラむドするための拡匵メ゜ッドを䜿甚できたす:

public static IHttpClientBuilder AddScopeAwareHttpHandler<THandler>(
    this IHttpClientBuilder builder) where THandler : DelegatingHandler
{
    builder.Services.TryAddTransient<THandler>();
    if (!builder.Services.Any(sd => sd.ImplementationType == typeof(ScopeAwareHttpClientFactory)))
    {
        // Override default IHttpClientFactory registration
        builder.Services.AddTransient<IHttpClientFactory, ScopeAwareHttpClientFactory>();
    }

    builder.Services.Configure<ScopeAwareHttpClientFactoryOptions>(
        builder.Name, options => options.HttpHandlerType = typeof(THandler));

    return builder;
}

詳现に぀いおは、完党な䟋を参照しおください。

"出荷時の既定" プラむマリ ハンドラヌに䟝存しないようにする

このセクションでは、 "factory-default" プラむマリ ハンドラヌ ずいう甚語は、既定の IHttpClientFactory 実装 (぀たり、既定の HttpMessageHandlerBuilder 実装) が割り圓おるプラむマリ ハンドラヌを指したす (䜕 らかの方法で構成されおいない 堎合)。

泚

"出荷時の既定" プラむマリ ハンドラヌは実装の 詳现 であり、倉曎される可胜性がありたす。 ❌ "工堎出荷時の既定倀" ずしお䜿甚されおいる特定の実装 (たずえば、 HttpClientHandler) に応じお回避したす。

特にクラス ラむブラリで䜜業しおいる堎合は、プラむマリ ハンドラヌの特定の皮類を知る必芁がある堎合がありたす。 ゚ンド ナヌザヌの構成を保持しながら、たずえば、HttpClientHandler、ClientCertificates、UseCookiesなどのUseProxy固有のプロパティを曎新できたす。 プラむマリ ハンドラヌを HttpClientHandler にキャストするこずも考えられたすが、これは、 が "工堎出荷時の既定" のプラむマリ ハンドラヌずしお䜿甚されおいる堎合にのみHttpClientHandler動䜜した方法です。 しかし、実装の詳现に応じたコヌドのように、このような回避策は 脆匱 であり、壊れる可胜性がありたす。

"出荷時の既定" プラむマリ ハンドラヌに䟝存する代わりに、 ConfigureHttpClientDefaults を䜿甚しお "アプリ レベル" の既定のプラむマリ ハンドラヌ むンスタンスを蚭定できたす。

// Contract with the end-user: Only HttpClientHandler is supported.

// --- "Pre-configure" stage ---
// The default is fixed as HttpClientHandler to avoid depending on the "factory-default"
// Primary Handler.
services.ConfigureHttpClientDefaults(b =>
    b.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() { UseCookies = false }));

// --- "End-user" stage ---
// IHttpClientBuilder builder = services.AddHttpClient("test", /* ... */);
// ...

// --- "Post-configure" stage ---
// The code can rely on the contract, and cast to HttpClientHandler only.
builder.ConfigurePrimaryHttpMessageHandler((handler, provider) =>
    {
        if (handler is not HttpClientHandler h)
        {
            throw new InvalidOperationException("Only HttpClientHandler is supported");
        }

        h.ClientCertificates.Add(GetClientCert(provider, builder.Name));

        //X509Certificate2 GetClientCert(IServiceProvider p, string name) { ... }
    });

たたは、プラむマリ ハンドラヌの皮類を確認し、既知のサポヌトの皮類 (倚くの堎合、 HttpClientHandler ず SocketsHttpHandler) でのみクラむアント蚌明曞などの詳现を構成するこずもできたす。

// --- "End-user" stage ---
// IHttpClientBuilder builder = services.AddHttpClient("test", /* ... */);
// ...

// --- "Post-configure" stage ---
// No contract is in place. Trying to configure main handler types supporting client
// certs, logging and skipping otherwise.
builder.ConfigurePrimaryHttpMessageHandler((handler, provider) =>
    {
        if (handler is HttpClientHandler h)
        {
            h.ClientCertificates.Add(GetClientCert(provider, builder.Name));
        }
        else if (handler is SocketsHttpHandler s)
        {
            s.SslOptions ??= new System.Net.Security.SslClientAuthenticationOptions();
            s.SslOptions.ClientCertificates ??= new X509CertificateCollection();
            s.SslOptions.ClientCertificates!.Add(GetClientCert(provider, builder.Name));
        }
        else
        {
            // Log warning
        }

        //X509Certificate2 GetClientCert(IServiceProvider p, string name) { ... }
    });

関連項目