ASP.NET Core で IHttpClientFactory を使用して HTTP 要求を行うMake HTTP requests using IHttpClientFactory in ASP.NET Core

寄稿者: Glenn CondronRyan NowakSteve GordonRick AndersonKirk LarkinBy Glenn Condron, Ryan Nowak, Steve Gordon, Rick Anderson, and Kirk Larkin

アプリ内で HttpClient インスタンスを構成して作成するために、IHttpClientFactory を登録して使用できます。An IHttpClientFactory can be registered and used to configure and create HttpClient instances in an app. IHttpClientFactory には次のような利点があります。IHttpClientFactory offers the following benefits:

  • 論理 HttpClient インスタンスの名前付けと構成を一元化します。Provides a central location for naming and configuring logical HttpClient instances. たとえば、 GitHub にアクセスするために、 github という名前のクライアントを登録して構成できます。For example, a client named github could be registered and configured to access GitHub. 一般的なアクセスのために、既定のクライアントを登録できます。A default client can be registered for general access.
  • HttpClient でのハンドラーのデリゲートにより、送信ミドルウェアの概念が体系化されます。Codifies the concept of outgoing middleware via delegating handlers in HttpClient. HttpClient でのハンドラーのデリゲートを利用するために、Polly ベースのミドルウェアに対する拡張機能が提供されます。Provides extensions for Polly-based middleware to take advantage of delegating handlers in HttpClient.
  • 基になっている HttpClientMessageHandler インスタンスのプールと有効期間が管理されます。Manages the pooling and lifetime of underlying HttpClientMessageHandler instances. 自動管理により、HttpClient の有効期間を手動で管理するときの一般的な DNS (ドメイン ネーム システム) の問題が発生しなくなります。Automatic management avoids common DNS (Domain Name System) problems that occur when manually managing HttpClient lifetimes.
  • ファクトリによって作成されたクライアントから送信されるすべての要求に対し、(ILogger によって) 構成可能なログ エクスペリエンスを追加します。Adds a configurable logging experience (via ILogger) for all requests sent through clients created by the factory.

サンプル コードを表示またはダウンロードします (ダウンロード方法)。View or download sample code (how to download).

このトピックのバージョンのサンプル コードでは、HTTP 応答で返された JSON コンテンツを、System.Text.Json を使用して逆シリアル化します。The sample code in this topic version uses System.Text.Json to deserialize JSON content returned in HTTP responses. Json.NET および ReadAsAsync<T> を使用するサンプルについては、バージョン セレクターを使用して、このトピックの 2.x バージョンを選択してください。For samples that use Json.NET and ReadAsAsync<T>, use the version selector to select a 2.x version of this topic.

利用パターンConsumption patterns

アプリで IHttpClientFactory を使用するには複数の方法があります。There are several ways IHttpClientFactory can be used in an app:

どの方法が最善かは、アプリの要件によって異なります。The best approach depends upon the app's requirements.

基本的な使用方法Basic usage

IHttpClientFactory は、AddHttpClient を呼び出すことによって登録できます。IHttpClientFactory can be registered by calling AddHttpClient:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpClient();
        // Remaining code deleted for brevity.

IHttpClientFactory は、依存関係の挿入 (DI) を使用して要求できます。An IHttpClientFactory can be requested using dependency injection (DI). 次のコードでは、IHttpClientFactory を使用して HttpClient インスタンスを作成しています。The following code uses IHttpClientFactory to create an HttpClient instance:

public class BasicUsageModel : PageModel
{
    private readonly IHttpClientFactory _clientFactory;

    public IEnumerable<GitHubBranch> Branches { get; private set; }

    public bool GetBranchesError { get; private set; }

    public BasicUsageModel(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }

    public async Task OnGet()
    {
        var request = new HttpRequestMessage(HttpMethod.Get,
            "https://api.github.com/repos/aspnet/AspNetCore.Docs/branches");
        request.Headers.Add("Accept", "application/vnd.github.v3+json");
        request.Headers.Add("User-Agent", "HttpClientFactory-Sample");

        var client = _clientFactory.CreateClient();

        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            using var responseStream = await response.Content.ReadAsStreamAsync();
            Branches = await JsonSerializer.DeserializeAsync
                <IEnumerable<GitHubBranch>>(responseStream);
        }
        else
        {
            GetBranchesError = true;
            Branches = Array.Empty<GitHubBranch>();
        }
    }
}

前の例のように IHttpClientFactory を使用するのは、既存のアプリをリファクタリングするのに適した方法です。Using IHttpClientFactory like in the preceding example is a good way to refactor an existing app. HttpClient の使用方法に対する影響はありません。It has no impact on how HttpClient is used. 既存のアプリで HttpClient のインスタンスが作成されている場所を、CreateClient の呼び出しに置き換えます。In places where HttpClient instances are created in an existing app, replace those occurrences with calls to CreateClient.

名前付きクライアントNamed clients

名前付きクライアントは、次の場合に適しています。Named clients are a good choice when:

  • アプリで、多くの異なる HttpClient を使用する必要がある。The app requires many distinct uses of HttpClient.
  • 多くの HttpClient の構成が異なる。Many HttpClients have different configuration.

名前付き HttpClient の構成は、Startup.ConfigureServices での登録時に指定できます。Configuration for a named HttpClient can be specified during registration in Startup.ConfigureServices:

services.AddHttpClient("github", c =>
{
    c.BaseAddress = new Uri("https://api.github.com/");
    // Github API versioning
    c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    // Github requires a user-agent
    c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});

上記のコードでは、クライアントに次のものが構成されます。In the preceding code the client is configured with:

  • ベース アドレス https://api.github.com/The base address https://api.github.com/.
  • GitHub API を使用するために必要な 2 つのヘッダー。Two headers required to work with the GitHub API.

CreateClientCreateClient

CreateClient が呼び出されるたびに、次のことが行われます。Each time CreateClient is called:

  • HttpClient の新しいインスタンスが作成されます。A new instance of HttpClient is created.
  • 構成アクションが呼び出されます。The configuration action is called.

名前付きクライアントを作成するには、その名前を CreateClient に渡します。To create a named client, pass its name into CreateClient:

public class NamedClientModel : PageModel
{
    private readonly IHttpClientFactory _clientFactory;

    public IEnumerable<GitHubPullRequest> PullRequests { get; private set; }

    public bool GetPullRequestsError { get; private set; }

    public bool HasPullRequests => PullRequests.Any();

    public NamedClientModel(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }

    public async Task OnGet()
    {
        var request = new HttpRequestMessage(HttpMethod.Get,
            "repos/aspnet/AspNetCore.Docs/pulls");

        var client = _clientFactory.CreateClient("github");

        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            using var responseStream = await response.Content.ReadAsStreamAsync();
            PullRequests = await JsonSerializer.DeserializeAsync
                    <IEnumerable<GitHubPullRequest>>(responseStream);
        }
        else
        {
            GetPullRequestsError = true;
            PullRequests = Array.Empty<GitHubPullRequest>();
        }
    }
}

上記のコードでは、要求でホスト名を指定する必要はありません。In the preceding code, the request doesn't need to specify a hostname. クライアントに構成されているベース アドレスが使用されるため、コードではパスを渡すだけで済みます。The code can pass just the path, since the base address configured for the client is used.

型指定されたクライアントTyped clients

型指定されたクライアント:Typed clients:

  • キーとして文字列を使用する必要なしに、名前付きクライアントと同じ機能を提供します。Provide the same capabilities as named clients without the need to use strings as keys.
  • クライアントを使用するときに、IntelliSense とコンパイラのヘルプが提供されます。Provides IntelliSense and compiler help when consuming clients.
  • 特定の HttpClient を構成してそれと対話する 1 つの場所を提供します。Provide a single location to configure and interact with a particular HttpClient. たとえば、単一の型指定されたクライアントは、次のために使用される場合があります。For example, a single typed client might be used:
    • 単一のバックエンド エンドポイント用。For a single backend endpoint.
    • エンドポイントを処理するすべてのロジックをカプセル化するため。To encapsulate all logic dealing with the endpoint.
  • DI に対応しており、アプリ内の必要な場所に挿入できます。Work with DI and can be injected where required in the app.

型指定されたクライアントは、そのコンストラクターで HttpClient パラメーターを受け取ります。A typed client accepts an HttpClient parameter in its constructor:

public class GitHubService
{
    public HttpClient Client { get; }

    public GitHubService(HttpClient client)
    {
        client.BaseAddress = new Uri("https://api.github.com/");
        // GitHub API versioning
        client.DefaultRequestHeaders.Add("Accept",
            "application/vnd.github.v3+json");
        // GitHub requires a user-agent
        client.DefaultRequestHeaders.Add("User-Agent",
            "HttpClientFactory-Sample");

        Client = client;
    }

    public async Task<IEnumerable<GitHubIssue>> GetAspNetDocsIssues()
    {
        var response = await Client.GetAsync(
            "/repos/aspnet/AspNetCore.Docs/issues?state=open&sort=created&direction=desc");

        response.EnsureSuccessStatusCode();

        using var responseStream = await response.Content.ReadAsStreamAsync();
        return await JsonSerializer.DeserializeAsync
            <IEnumerable<GitHubIssue>>(responseStream);
    }
}

コードのコメントを英語以外の言語に翻訳し表示したい場合、こちらの GitHub ディスカッション イシューにてお知らせください。If you would like to see code comments translated to languages other than English, let us know in this GitHub discussion issue.

上のコードでは以下の操作が行われます。In the preceding code:

  • 構成が型指定されたクライアントに移動されます。The configuration is moved into the typed client.
  • HttpClient オブジェクトは、パブリック プロパティとして公開されます。The HttpClient object is exposed as a public property.

HttpClient の機能を公開する API 固有のメソッドを作成できます。API-specific methods can be created that expose HttpClient functionality. たとえば、GetAspNetDocsIssues メソッドでは、未解決の問題を取得するためのコードがカプセル化されています。For example, the GetAspNetDocsIssues method encapsulates code to retrieve open issues.

次のコードでは、Startup.ConfigureServices 内で AddHttpClient を呼び出して、型指定されたクライアント クラスを登録しています。The following code calls AddHttpClient in Startup.ConfigureServices to register a typed client class:

services.AddHttpClient<GitHubService>();

型指定されたクライアントは、DI で一時的として登録されます。The typed client is registered as transient with DI. 上記のコードで、AddHttpClientGitHubService を一時的なサービスとして登録します。In the preceding code, AddHttpClient registers GitHubService as a transient service. この登録では、ファクトリ メソッドを使用して次のことを行います。This registration uses a factory method to:

  1. HttpClient のインスタンスを作成します。Create an instance of HttpClient.
  2. GitHubService のインスタンスを作成し、HttpClient のインスタンスをそのコンストラクターに渡します。Create an instance of GitHubService, passing in the instance of HttpClient to its constructor.

型指定されたクライアントは、直接挿入して使用できます。The typed client can be injected and consumed directly:

public class TypedClientModel : PageModel
{
    private readonly GitHubService _gitHubService;

    public IEnumerable<GitHubIssue> LatestIssues { get; private set; }

    public bool HasIssue => LatestIssues.Any();

    public bool GetIssuesError { get; private set; }

    public TypedClientModel(GitHubService gitHubService)
    {
        _gitHubService = gitHubService;
    }

    public async Task OnGet()
    {
        try
        {
            LatestIssues = await _gitHubService.GetAspNetDocsIssues();
        }
        catch(HttpRequestException)
        {
            GetIssuesError = true;
            LatestIssues = Array.Empty<GitHubIssue>();
        }
    }
}

型指定されたクライアントのコンストラクターではなく、Startup.ConfigureServices での登録時に型指定されたクライアントの構成を指定できます。The configuration for a typed client can be specified during registration in Startup.ConfigureServices, rather than in the typed client's constructor:

services.AddHttpClient<RepoService>(c =>
{
    c.BaseAddress = new Uri("https://api.github.com/");
    c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});

HttpClient は、型指定されたクライアント内にカプセル化できます。The HttpClient can be encapsulated within a typed client. プロパティとして公開するのではなく、HttpClient インスタンスを内部的に呼び出すパブリック メソッドを定義します。Rather than exposing it as a property, define a method which calls the HttpClient instance internally:

public class RepoService
{
    // _httpClient isn't exposed publicly
    private readonly HttpClient _httpClient;

    public RepoService(HttpClient client)
    {
        _httpClient = client;
    }

    public async Task<IEnumerable<string>> GetRepos()
    {
        var response = await _httpClient.GetAsync("aspnet/repos");

        response.EnsureSuccessStatusCode();

        using var responseStream = await response.Content.ReadAsStreamAsync();
        return await JsonSerializer.DeserializeAsync
            <IEnumerable<string>>(responseStream);
    }
}

上記のコードでは、HttpClient はプライベート フィールドに格納されます。In the preceding code, the HttpClient is stored in a private field. HttpClient へのアクセスは、パブリック GetRepos メソッドによって行われます。Access to the HttpClient is by the public GetRepos method.

生成されたクライアントGenerated clients

IHttpClientFactory は、Refit などのサードパーティ製ライブラリと組み合わせて使用できます。IHttpClientFactory can be used in combination with third-party libraries such as Refit. Refit は、.NET 用の REST ライブラリです。Refit is a REST library for .NET. REST API をライブ インターフェイスに変換します。It converts REST APIs into live interfaces. インターフェイスの実装は RestService によって動的に生成され、HttpClient を使用して外部 HTTP の呼び出しを行います。An implementation of the interface is generated dynamically by the RestService, using HttpClient to make the external HTTP calls.

インターフェイスと応答は、外部の API とその応答を表すように定義されます。An interface and a reply are defined to represent the external API and its response:

public interface IHelloClient
{
    [Get("/helloworld")]
    Task<Reply> GetMessageAsync();
}

public class Reply
{
    public string Message { get; set; }
}

型指定されたクライアントを追加し、Refit を使用して実装を生成できます。A typed client can be added, using Refit to generate the implementation:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient("hello", c =>
    {
        c.BaseAddress = new Uri("http://localhost:5000");
    })
    .AddTypedClient(c => Refit.RestService.For<IHelloClient>(c));

    services.AddControllers();
}

定義されたインターフェイスは、DI および Refit によって提供される実装で必要に応じて使用できます。The defined interface can be consumed where necessary, with the implementation provided by DI and Refit:

[ApiController]
public class ValuesController : ControllerBase
{
    private readonly IHelloClient _client;

    public ValuesController(IHelloClient client)
    {
        _client = client;
    }

    [HttpGet("/")]
    public async Task<ActionResult<Reply>> Index()
    {
        return await _client.GetMessageAsync();
    }
}

POST、PUT、DELETE の要求を行うMake POST, PUT, and DELETE requests

前の例では、すべての HTTP 要求で GET HTTP 動詞が使用されています。In the preceding examples, all HTTP requests use the GET HTTP verb. HttpClient では、次のような他の HTTP 動詞もサポートされています。HttpClient also supports other HTTP verbs, including:

  • POSTPOST
  • PUTPUT
  • DelDELETE
  • PATCHPATCH

サポートされている HTTP 動詞の一覧については、「HttpMethod」を参照してください。For a complete list of supported HTTP verbs, see HttpMethod.

次の例は、HTTP POST 要求の方法を示しています。The following example shows how to make an HTTP POST request:

public async Task CreateItemAsync(TodoItem todoItem)
{
    var todoItemJson = new StringContent(
        JsonSerializer.Serialize(todoItem, _jsonSerializerOptions),
        Encoding.UTF8,
        "application/json");

    using var httpResponse =
        await _httpClient.PostAsync("/api/TodoItems", todoItemJson);

    httpResponse.EnsureSuccessStatusCode();
}

上記のコードの CreateItemAsync メソッドは:In the preceding code, the CreateItemAsync method:

  • System.Text.Json を使用して TodoItem パラメーターを JSON にシリアル化します。Serializes the TodoItem parameter to JSON using System.Text.Json. これは、JsonSerializerOptions のインスタンスを使用して、シリアル化プロセスを構成します。This uses an instance of JsonSerializerOptions to configure the serialization process.
  • HTTP 要求の本文で送信するためにシリアル化された JSON をパッケージ化する StringContent のインスタンスを作成します。Creates an instance of StringContent to package the serialized JSON for sending in the HTTP request's body.
  • PostAsync を呼び出して、指定した URL に JSON の内容を送信します。Calls PostAsync to send the JSON content to the specified URL. これは HttpClient.BaseAddress に追加される相対 URL です。This is a relative URL that gets added to the HttpClient.BaseAddress.
  • 応答状態コードが成功を示していない場合に、EnsureSuccessStatusCode を呼び出して例外をスローします。Calls EnsureSuccessStatusCode to throw an exception if the response status code does not indicate success.

HttpClient は、他の種類のコンテンツもサポートしています。HttpClient also supports other types of content. たとえば、MultipartContentStreamContent です。For example, MultipartContent and StreamContent. サポートされているコンテンツの一覧については、「HttpContent」を参照してください。For a complete list of supported content, see HttpContent.

HTTP PUT 要求の例を次に示します。The following example shows an HTTP PUT request:

public async Task SaveItemAsync(TodoItem todoItem)
{
    var todoItemJson = new StringContent(
        JsonSerializer.Serialize(todoItem),
        Encoding.UTF8,
        "application/json");

    using var httpResponse =
        await _httpClient.PutAsync($"/api/TodoItems/{todoItem.Id}", todoItemJson);

    httpResponse.EnsureSuccessStatusCode();
}

上記のコードは、POST の例によく似ています。The preceding code is very similar to the POST example. SaveItemAsync メソッドは PostAsync ではなく PutAsync を呼び出します。The SaveItemAsync method calls PutAsync instead of PostAsync.

HTTP DELETE 要求の例を次に示します。The following example shows an HTTP DELETE request:

public async Task DeleteItemAsync(long itemId)
{
    using var httpResponse =
        await _httpClient.DeleteAsync($"/api/TodoItems/{itemId}");

    httpResponse.EnsureSuccessStatusCode();
}

上記のコードの DeleteItemAsync メソッドは DeleteAsync を呼び出します。In the preceding code, the DeleteItemAsync method calls DeleteAsync. HTTP DELETE 要求には通常、本文が含まれていないため、DeleteAsync メソッドは HttpContent のインスタンスを受け入れるオーバーロードを提供しません。Because HTTP DELETE requests typically contain no body, the DeleteAsync method doesn't provide an overload that accepts an instance of HttpContent.

HttpClient でのさまざまな HTTP 動詞の使用の詳細については、「HttpClient」を参照してください。To learn more about using different HTTP verbs with HttpClient, see HttpClient.

送信要求ミドルウェアOutgoing request middleware

HttpClient は、送信 HTTP 要求用にリンクできるハンドラーのデリゲートの概念を備えています。HttpClient has the concept of delegating handlers that can be linked together for outgoing HTTP requests. IHttpClientFactory:IHttpClientFactory:

  • 各名前付きクライアントに適用するハンドラーの定義が簡単になります。Simplifies defining the handlers to apply for each named client.

  • 送信要求ミドルウェア パイプラインを構築するための複数のハンドラーの登録とチェーン化がサポートされています。Supports registration and chaining of multiple handlers to build an outgoing request middleware pipeline. 各ハンドラーは、送信要求の前と後に処理を実行できます。Each of these handlers is able to perform work before and after the outgoing request. このパターンは次のようなものです。This pattern:

    • ASP.NET Core での受信ミドルウェア パイプラインに似ています。Is similar to the inbound middleware pipeline in ASP.NET Core.

    • 次のような HTTP 要求に関する横断的な問題を管理するためのメカニズムが提供されます。Provides a mechanism to manage cross-cutting concerns around HTTP requests, such as:

      • キャッシュcaching
      • エラー処理error handling
      • シリアル化serialization
      • ログlogging

デリゲート ハンドラーを作成するには、次のようにします。To create a delegating handler:

  • DelegatingHandler から派生します。Derive from DelegatingHandler.
  • SendAsync をオーバーライドします。Override SendAsync. パイプライン内の次のハンドラーに要求を渡す前に、コードを実行します。Execute code before passing the request to the next handler in the pipeline:
public class ValidateHeaderHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        if (!request.Headers.Contains("X-API-KEY"))
        {
            return new HttpResponseMessage(HttpStatusCode.BadRequest)
            {
                Content = new StringContent(
                    "You must supply an API key header called X-API-KEY")
            };
        }

        return await base.SendAsync(request, cancellationToken);
    }
}

上記のコードでは、X-API-KEY ヘッダーが要求に含まれているかどうかが確認されます。The preceding code checks if the X-API-KEY header is in the request. X-API-KEY がない場合は、BadRequest が返されます。If X-API-KEY is missing, BadRequest is returned.

Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandler を使用して HttpClient の構成に複数のハンドラーを追加できます。More than one handler can be added to the configuration for an HttpClient with Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandler:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<ValidateHeaderHandler>();

    services.AddHttpClient("externalservice", c =>
    {
        // Assume this is an "external" service which requires an API KEY
        c.BaseAddress = new Uri("https://localhost:5001/");
    })
    .AddHttpMessageHandler<ValidateHeaderHandler>();

    // Remaining code deleted for brevity.

上記のコードでは、ValidateHeaderHandler が DI で登録されます。In the preceding code, the ValidateHeaderHandler is registered with DI. IHttpClientFactory では、ハンドラーごとに個別の DI スコープが作成されます。The IHttpClientFactory creates a separate DI scope for each handler. ハンドラーは、任意のスコープのサービスに依存することができます。Handlers can depend upon services of any scope. ハンドラーが依存するサービスは、ハンドラーが破棄されるときに破棄されます。Services that handlers depend upon are disposed when the handler is disposed.

登録が済むと、AddHttpMessageHandler を呼び出してハンドラーの型を渡すことができます。Once registered, AddHttpMessageHandler can be called, passing in the type for the handler.

実行する順序で、複数のハンドラーを登録することができます。Multiple handlers can be registered in the order that they should execute. 最後の HttpClientHandler が要求を実行するまで、各ハンドラーは次のハンドラーをラップします。Each handler wraps the next handler until the final HttpClientHandler executes the request:

services.AddTransient<SecureRequestHandler>();
services.AddTransient<RequestDataHandler>();

services.AddHttpClient("clientwithhandlers")
    // This handler is on the outside and called first during the 
    // request, last during the response.
    .AddHttpMessageHandler<SecureRequestHandler>()
    // This handler is on the inside, closest to the request being 
    // sent.
    .AddHttpMessageHandler<RequestDataHandler>();

次の方法のいずれかを使って、要求ごとの状態をメッセージ ハンドラーと共有します。Use one of the following approaches to share per-request state with message handlers:

Polly ベースのハンドラーを使用するUse Polly-based handlers

IHttpClientFactory は、サードパーティのライブラリ Polly と統合します。IHttpClientFactory integrates with the third-party library Polly. Polly は、.NET 用の包括的な回復力および一時的エラー処理ライブラリです。Polly is a comprehensive resilience and transient fault-handling library for .NET. 開発者は、自然でスレッド セーフな方法を使用して、Retry、Circuit Breaker、Timeout、Bulkhead Isolation、Fallback などのポリシーを表現できます。It allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.

構成されている HttpClient インスタンスで Polly ポリシーを使用できるようにするための、拡張メソッドが提供されています。Extension methods are provided to enable the use of Polly policies with configured HttpClient instances. Polly の拡張機能では、クライアントへの Polly ベースのハンドラーの追加がサポートされています。The Polly extensions support adding Polly-based handlers to clients. Polly では、Microsoft.Extensions.Http.Polly NuGet パッケージが必要です。Polly requires the Microsoft.Extensions.Http.Polly NuGet package.

一時的な障害を処理するHandle transient faults

通常、外部 HTTP を呼び出すときに発生する障害は、一時的なものです。Faults typically occur when external HTTP calls are transient. AddTransientHttpErrorPolicy では、一時的なエラーを処理するためのポリシーを定義できます。AddTransientHttpErrorPolicy allows a policy to be defined to handle transient errors. AddTransientHttpErrorPolicy で構成されたポリシーでは、次の応答が処理されます。Policies configured with AddTransientHttpErrorPolicy handle the following responses:

AddTransientHttpErrorPolicy では、可能性のある一時的障害を表すエラーを処理するために構成された PolicyBuilder オブジェクトへのアクセスが提供されます。AddTransientHttpErrorPolicy provides access to a PolicyBuilder object configured to handle errors representing a possible transient fault:

public void ConfigureServices(IServiceCollection services)
{           
    services.AddHttpClient<UnreliableEndpointCallerService>()
        .AddTransientHttpErrorPolicy(p => 
            p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));

    // Remaining code deleted for brevity.

上記のコードでは、WaitAndRetryAsync ポリシーが定義されています。In the preceding code, a WaitAndRetryAsync policy is defined. 失敗した要求は最大 3 回再試行され、再試行の間には 600 ミリ秒の遅延が設けられます。Failed requests are retried up to three times with a delay of 600 ms between attempts.

ポリシーを動的に選択するDynamically select policies

Polly ベースのハンドラーを追加するための拡張メソッドが提供されています (AddPolicyHandler など)。Extension methods are provided to add Polly-based handlers, for example, AddPolicyHandler. 次の AddPolicyHandler のオーバーロードでは、要求が検査されて、適用するポリシーが決定されます。The following AddPolicyHandler overload inspects the request to decide which policy to apply:

var timeout = Policy.TimeoutAsync<HttpResponseMessage>(
    TimeSpan.FromSeconds(10));
var longTimeout = Policy.TimeoutAsync<HttpResponseMessage>(
    TimeSpan.FromSeconds(30));

services.AddHttpClient("conditionalpolicy")
// Run some code to select a policy based on the request
    .AddPolicyHandler(request => 
        request.Method == HttpMethod.Get ? timeout : longTimeout);

上記のコードでは、送信要求が HTTP GET の場合は、10 秒のタイムアウトが適用されます。In the preceding code, if the outgoing request is an HTTP GET, a 10-second timeout is applied. 他の HTTP メソッドの場合は、30 秒のタイムアウトが使用されます。For any other HTTP method, a 30-second timeout is used.

複数の Polly ハンドラーを追加するAdd multiple Polly handlers

Polly のポリシーは入れ子にするのが一般的です。It's common to nest Polly policies:

services.AddHttpClient("multiplepolicies")
    .AddTransientHttpErrorPolicy(p => p.RetryAsync(3))
    .AddTransientHttpErrorPolicy(
        p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));

前の例の場合:In the preceding example:

  • 2 つのハンドラーが追加されます。Two handlers are added.
  • 1 つ目のハンドラーでは、AddTransientHttpErrorPolicy を使用して再試行ポリシーが追加されます。The first handler uses AddTransientHttpErrorPolicy to add a retry policy. 失敗した要求は 3 回まで再試行されます。Failed requests are retried up to three times.
  • 2 つ目の AddTransientHttpErrorPolicy の呼び出しでは、サーキット ブレーカー ポリシーが追加されます。The second AddTransientHttpErrorPolicy call adds a circuit breaker policy. 試行が連続して 5 回失敗した場合、それ以上の外部要求は 30 秒間ブロックされます。Further external requests are blocked for 30 seconds if 5 failed attempts occur sequentially. サーキット ブレーカー ポリシーはステートフルです。Circuit breaker policies are stateful. このクライアントからのすべての呼び出しは、同じサーキット状態を共有します。All calls through this client share the same circuit state.

Polly レジストリからポリシーを追加するAdd policies from the Polly registry

定期的に使用されるポリシーを管理するには、ポリシーを 1 回定義した後、PolicyRegistry でポリシーを登録します。An approach to managing regularly used policies is to define them once and register them with a PolicyRegistry.

次のコードの内容は以下のとおりです。In the following code:

  • "regular" ポリシーと "long" ポリシーが追加されます。The "regular" and "long" policies are added.
  • AddPolicyHandlerFromRegistry では、レジストリから "regular" ポリシーと "long" ポリシーが追加されます。AddPolicyHandlerFromRegistry adds the "regular" and "long" policies from the registry.
public void ConfigureServices(IServiceCollection services)
{           
    var timeout = Policy.TimeoutAsync<HttpResponseMessage>(
        TimeSpan.FromSeconds(10));
    var longTimeout = Policy.TimeoutAsync<HttpResponseMessage>(
        TimeSpan.FromSeconds(30));
    
    var registry = services.AddPolicyRegistry();

    registry.Add("regular", timeout);
    registry.Add("long", longTimeout);
    
    services.AddHttpClient("regularTimeoutHandler")
        .AddPolicyHandlerFromRegistry("regular");

    services.AddHttpClient("longTimeoutHandler")
       .AddPolicyHandlerFromRegistry("long");

    // Remaining code deleted for brevity.

IHttpClientFactory と Polly の統合の詳細については、Polly に関する wiki を参照してください。For more information on IHttpClientFactory and Polly integrations, see the Polly wiki.

HttpClient と有効期間の管理HttpClient and lifetime management

IHttpClientFactoryCreateClient を呼び出すたびに、HttpClient の新しいインスタンスが返されます。A new HttpClient instance is returned each time CreateClient is called on the IHttpClientFactory. HttpMessageHandler は、名前付きクライアントごとに作成されます。An HttpMessageHandler is created per named client. ファクトリによって HttpMessageHandler インスタンスの有効期間が管理されます。The factory manages the lifetimes of the HttpMessageHandler instances.

IHttpClientFactory は、リソースの消費量を減らすため、ファクトリによって作成された HttpMessageHandler のインスタンスをプールします。IHttpClientFactory pools the HttpMessageHandler instances created by the factory to reduce resource consumption. 新しい HttpClient インスタンスを作成するときに、プールの HttpMessageHandler インスタンスの有効期間が切れていない場合はそれを再利用する場合があります。An HttpMessageHandler instance may be reused from the pool when creating a new HttpClient instance if its lifetime hasn't expired.

通常、各ハンドラーでは基になる HTTP 接続が独自に管理されるため、ハンドラーはプールすることが望まれます。Pooling of handlers is desirable as each handler typically manages its own underlying HTTP connections. 必要以上のハンドラーを作成すると、接続に遅延が発生する可能性があります。Creating more handlers than necessary can result in connection delays. また、一部のハンドラーでは接続が無期限に開かれており、DNS (ドメイン ネーム システム) の変更にハンドラーが対応できないことがあります。Some handlers also keep connections open indefinitely, which can prevent the handler from reacting to DNS (Domain Name System) changes.

ハンドラーの既定の有効期間は 2 分です。The default handler lifetime is two minutes. 名前付きクライアントごとに、既定値をオーバーライドすることができます。The default value can be overridden on a per named client basis:

public void ConfigureServices(IServiceCollection services)
{           
    services.AddHttpClient("extendedhandlerlifetime")
        .SetHandlerLifetime(TimeSpan.FromMinutes(5));

    // Remaining code deleted for brevity.

通常、HttpClient インスタンスは、破棄する必要の ない .NET オブジェクトとして扱うことができます。HttpClient instances can generally be treated as .NET objects not requiring disposal. 破棄すると送信要求がキャンセルされ、Dispose の呼び出し後には指定された HttpClient インスタンスを使用できなくなります。Disposal cancels outgoing requests and guarantees the given HttpClient instance can't be used after calling Dispose. IHttpClientFactory は、HttpClient インスタンスによって使用されたリソースの追跡と破棄を行います。IHttpClientFactory tracks and disposes resources used by HttpClient instances.

IHttpClientFactory の登場以前は、1 つの HttpClient インスタンスを長い期間使い続けるパターンが一般的に使用されていました。Keeping a single HttpClient instance alive for a long duration is a common pattern used before the inception of IHttpClientFactory. IHttpClientFactory に移行した後は、このパターンは不要になります。This pattern becomes unnecessary after migrating to IHttpClientFactory.

IHttpClientFactory の代替手段Alternatives to IHttpClientFactory

DI 対応のアプリ内で IHttpClientFactory を使用すれば、次のことを回避できます。Using IHttpClientFactory in a DI-enabled app avoids:

  • HttpMessageHandler インスタンスをプールすることによるリソース枯渇の問題。Resource exhaustion problems by pooling HttpMessageHandler instances.
  • 一定の間隔で HttpMessageHandler インスタンスを循環させることによって発生する古くなった DNS の問題。Stale DNS problems by cycling HttpMessageHandler instances at regular intervals.

有効期間の長い SocketsHttpHandler インスタンスを使用して、上記の問題を解決する別の方法があります。There are alternative ways to solve the preceding problems using a long-lived SocketsHttpHandler instance.

  • アプリの起動時に SocketsHttpHandler インスタンスを作成し、アプリの有効期間中、それを使用します。Create an instance of SocketsHttpHandler when the app starts and use it for the life of the app.
  • DNS の更新時間に基づいて、PooledConnectionLifetime を適切な値に構成します。Configure PooledConnectionLifetime to an appropriate value based on DNS refresh times.
  • 必要に応じて new HttpClient(handler, disposeHandler: false) を使用して HttpClient インスタンスを作成します。Create HttpClient instances using new HttpClient(handler, disposeHandler: false) as needed.

上記の方法を使用すると、IHttpClientFactory が同様の方法で解決するリソース管理の問題を解決できます。The preceding approaches solve the resource management problems that IHttpClientFactory solves in a similar way.

  • SocketsHttpHandler を使用すると、HttpClient インスタンス間で接続を共有できます。The SocketsHttpHandler shares connections across HttpClient instances. この共有によってソケットの枯渇が防止されます。This sharing prevents socket exhaustion.
  • SocketsHttpHandler では、古くなった DNS の問題を回避するために PooledConnectionLifetime に従って接続を循環されます。The SocketsHttpHandler cycles connections according to PooledConnectionLifetime to avoid stale DNS problems.

CookiesCookies

HttpMessageHandler インスタンスをプールすると、CookieContainer オブジェクトが共有されます。The pooled HttpMessageHandler instances results in CookieContainer objects being shared. 予期せぬ CookieContainer オブジェクト共有があると、多くの場合、コードは不適切なものとなります。Unanticipated CookieContainer object sharing often results in incorrect code. cookie を必要とするアプリの場合は、次のいずれかを検討してください。For apps that require cookies, consider either:

  • 自動的な cookie 処理の無効化Disabling automatic cookie handling
  • IHttpClientFactory の回避Avoiding IHttpClientFactory

ConfigurePrimaryHttpMessageHandler を呼び出して、自動的な cookie 処理を無効にします。Call ConfigurePrimaryHttpMessageHandler to disable automatic cookie handling:

services.AddHttpClient("configured-disable-automatic-cookies")
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        return new SocketsHttpHandler()
        {
            UseCookies = false,
        };
    });

ログの記録Logging

IHttpClientFactory によって作成されたクライアントは、すべての要求のログ メッセージを記録します。Clients created via IHttpClientFactory record log messages for all requests. 既定のログ メッセージを見るには、ログの構成で適切な情報レベルを有効にします。Enable the appropriate information level in the logging configuration to see the default log messages. 要求ヘッダーのログなどの追加ログは、トレース レベルでのみ含まれます。Additional logging, such as the logging of request headers, is only included at trace level.

各クライアントに使用されるログのカテゴリには、クライアントの名前が含まれます。The log category used for each client includes the name of the client. たとえば、 MyNamedClient という名前のクライアントでは、"System.Net.Http.HttpClient. MyNamedClient.LogicalHandler" というカテゴリでメッセージがログに記録されます。A client named MyNamedClient , for example, logs messages with a category of "System.Net.Http.HttpClient. MyNamedClient.LogicalHandler". LogicalHandler というサフィックスが付いたメッセージが、要求ハンドラーのパイプラインの外部で発生します。Messages suffixed with LogicalHandler occur outside the request handler pipeline. 要求では、パイプライン内の他のハンドラーが要求を処理する前に、メッセージがログに記録されます。On the request, messages are logged before any other handlers in the pipeline have processed it. 応答では、他のパイプライン ハンドラーが応答を受け取った後で、メッセージがログに記録されます。On the response, messages are logged after any other pipeline handlers have received the response.

ログ記録は、要求ハンドラーのパイプラインの内部でも行われます。Logging also occurs inside the request handler pipeline. MyNamedClient の例では、これらのメッセージはログ カテゴリ "System.Net.Http.HttpClient. MyNamedClient.ClientHandler" でログに記録されます。In the MyNamedClient example, those messages are logged with the log category "System.Net.Http.HttpClient. MyNamedClient.ClientHandler". 要求では、他のすべてのハンドラーが実行された後、要求が送信される直前に、これが行われます。For the request, this occurs after all other handlers have run and immediately before the request is sent. 応答では、このログ記録には、ハンドラー パイプラインを通じ戻される前の応答の状態が含まれます。On the response, this logging includes the state of the response before it passes back through the handler pipeline.

パイプラインの外部と内部でログを有効にすると、他のパイプライン ハンドラーによる変更を検査できます。Enabling logging outside and inside the pipeline enables inspection of the changes made by the other pipeline handlers. これには、要求ヘッダーや応答状態コードへの変更を含めることができます。This may include changes to request headers or to the response status code.

ログのカテゴリにクライアントの名前を含めると、特定の名前付きクライアントでログをフィルター処理できます。Including the name of the client in the log category enables log filtering for specific named clients.

HttpMessageHandler を構成するConfigure the HttpMessageHandler

クライアントによって使用される内部 HttpMessageHandler の構成を制御することが必要な場合があります。It may be necessary to control the configuration of the inner HttpMessageHandler used by a client.

名前付きクライアントまたは型指定されたクライアントを追加すると、IHttpClientBuilder が返されます。An IHttpClientBuilder is returned when adding named or typed clients. ConfigurePrimaryHttpMessageHandler 拡張メソッドを使用して、デリゲートを定義することができます。The ConfigurePrimaryHttpMessageHandler extension method can be used to define a delegate. デリゲートは、そのクライアントによって使用されるプライマリ HttpMessageHandler の作成と構成に使用されます。The delegate is used to create and configure the primary HttpMessageHandler used by that client:

public void ConfigureServices(IServiceCollection services)
{            
    services.AddHttpClient("configured-inner-handler")
        .ConfigurePrimaryHttpMessageHandler(() =>
        {
            return new HttpClientHandler()
            {
                AllowAutoRedirect = false,
                UseDefaultCredentials = true
            };
        });

    // Remaining code deleted for brevity.

コンソール アプリで IHttpClientFactory を使用するUse IHttpClientFactory in a console app

コンソール アプリで、次のパッケージ参照をプロジェクトに追加します。In a console app, add the following package references to the project:

次に例を示します。In the following example:

  • IHttpClientFactory汎用ホストのサービス コンテナーに登録されます。IHttpClientFactory is registered in the Generic Host's service container.
  • MyService により、クライアント ファクトリ インスタンスがサービスから作成され、それが HttpClient の作成に使用されます。MyService creates a client factory instance from the service, which is used to create an HttpClient. HttpClient は Web ページを取得する目的で使用されます。HttpClient is used to retrieve a webpage.
  • Main により、サービスの GetPage メソッドを実行し、Web ページ コンテンツの最初の 500 文字をコンソールに書き込むためのスコープが作成されます。Main creates a scope to execute the service's GetPage method and write the first 500 characters of the webpage content to the console.
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
class Program
{
    static async Task<int> Main(string[] args)
    {
        var builder = new HostBuilder()
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHttpClient();
                services.AddTransient<IMyService, MyService>();
            }).UseConsoleLifetime();

        var host = builder.Build();

        using (var serviceScope = host.Services.CreateScope())
        {
            var services = serviceScope.ServiceProvider;

            try
            {
                var myService = services.GetRequiredService<IMyService>();
                var pageContent = await myService.GetPage();

                Console.WriteLine(pageContent.Substring(0, 500));
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();

                logger.LogError(ex, "An error occurred.");
            }
        }

        return 0;
    }

    public interface IMyService
    {
        Task<string> GetPage();
    }

    public class MyService : IMyService
    {
        private readonly IHttpClientFactory _clientFactory;

        public MyService(IHttpClientFactory clientFactory)
        {
            _clientFactory = clientFactory;
        }

        public async Task<string> GetPage()
        {
            // Content from BBC One: Dr. Who website (©BBC)
            var request = new HttpRequestMessage(HttpMethod.Get,
                "https://www.bbc.co.uk/programmes/b006q2x0");
            var client = _clientFactory.CreateClient();
            var response = await client.SendAsync(request);

            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadAsStringAsync();
            }
            else
            {
                return $"StatusCode: {response.StatusCode}";
            }
        }
    }
}

ヘッダー伝達ミドルウェアHeader propagation middleware

ヘッダー伝達は、受信要求から送信 HTTP クライアント要求に HTTP ヘッダーを伝達するための ASP.NET Core ミドルウェアです。Header propagation is an ASP.NET Core middleware to propagate HTTP headers from the incoming request to the outgoing HTTP Client requests. ヘッダー伝達を使用するには、次を行います。To use header propagation:

  • Microsoft.AspNetCore.HeaderPropagation パッケージを参照します。Reference the Microsoft.AspNetCore.HeaderPropagation package.

  • Startup でミドルウェアと HttpClient を構成します。Configure the middleware and HttpClient in Startup:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
    
        services.AddHttpClient("MyForwardingClient").AddHeaderPropagation();
        services.AddHeaderPropagation(options =>
        {
            options.Headers.Add("X-TraceId");
        });
    }
    
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
    
        app.UseHttpsRedirection();
    
        app.UseHeaderPropagation();
    
        app.UseRouting();
    
        app.UseAuthorization();
    
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
    
  • クライアントで、構成されたヘッダーが送信要求に含まれます。The client includes the configured headers on outbound requests:

    var client = clientFactory.CreateClient("MyForwardingClient");
    var response = client.GetAsync(...);
    

その他の技術情報Additional resources

寄稿者: Glenn CondronRyan NowakSteve GordonBy Glenn Condron, Ryan Nowak, and Steve Gordon

アプリ内で HttpClient インスタンスを構成して作成するために、IHttpClientFactory を登録して使用できます。An IHttpClientFactory can be registered and used to configure and create HttpClient instances in an app. 次のような利点があります。It offers the following benefits:

  • 論理 HttpClient インスタンスの名前付けと構成を一元化します。Provides a central location for naming and configuring logical HttpClient instances. たとえば、 github クライアントを登録して、 GitHub にアクセスするように構成できます。For example, a github client can be registered and configured to access GitHub. 既定のクライアントは、他の目的に登録できます。A default client can be registered for other purposes.
  • HttpClient でのハンドラーのデリゲートにより送信ミドルウェアの概念を体系化し、Polly ベースのミドルウェアでそれを利用するための拡張機能を提供します。Codifies the concept of outgoing middleware via delegating handlers in HttpClient and provides extensions for Polly-based middleware to take advantage of that.
  • 基になっている HttpClientMessageHandler インスタンスのプールと有効期間を管理し、HttpClient の有効期間を手動で管理するときに発生する一般的な DNS の問題を防ぎます。Manages the pooling and lifetime of underlying HttpClientMessageHandler instances to avoid common DNS problems that occur when manually managing HttpClient lifetimes.
  • ファクトリによって作成されたクライアントから送信されるすべての要求に対し、(ILogger によって) 構成可能なログ エクスペリエンスを追加します。Adds a configurable logging experience (via ILogger) for all requests sent through clients created by the factory.

サンプル コードを表示またはダウンロードします (ダウンロード方法)。View or download sample code (how to download)

利用パターンConsumption patterns

アプリで IHttpClientFactory を使用するには複数の方法があります。There are several ways IHttpClientFactory can be used in an app:

これらの間に厳密な優劣はありません。None of them are strictly superior to another. どの方法が最善かは、アプリの制約に依存します。The best approach depends upon the app's constraints.

基本的な使用方法Basic usage

IHttpClientFactory は、Startup.ConfigureServices メソッドの内部で IServiceCollectionAddHttpClient 拡張メソッドを呼び出すことによって登録できます。The IHttpClientFactory can be registered by calling the AddHttpClient extension method on the IServiceCollection, inside the Startup.ConfigureServices method.

services.AddHttpClient();

登録が済むと、コードは、依存関係の挿入 (DI) を使用してサービスを挿入できる任意の場所で、IHttpClientFactory を受け取ることができます。Once registered, code can accept an IHttpClientFactory anywhere services can be injected with dependency injection (DI). IHttpClientFactory を使用して、HttpClient インスタンスを作成できます。The IHttpClientFactory can be used to create an HttpClient instance:

public class BasicUsageModel : PageModel
{
    private readonly IHttpClientFactory _clientFactory;

    public IEnumerable<GitHubBranch> Branches { get; private set; }

    public bool GetBranchesError { get; private set; }

    public BasicUsageModel(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }

    public async Task OnGet()
    {
        var request = new HttpRequestMessage(HttpMethod.Get, 
            "https://api.github.com/repos/aspnet/AspNetCore.Docs/branches");
        request.Headers.Add("Accept", "application/vnd.github.v3+json");
        request.Headers.Add("User-Agent", "HttpClientFactory-Sample");

        var client = _clientFactory.CreateClient();

        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            Branches = await response.Content
                .ReadAsAsync<IEnumerable<GitHubBranch>>();
        }
        else
        {
            GetBranchesError = true;
            Branches = Array.Empty<GitHubBranch>();
        }                               
    }
}

このように IHttpClientFactory を使用するのは、既存のアプリをリファクタリングする優れた方法です。Using IHttpClientFactory in this fashion is a good way to refactor an existing app. HttpClient の使用方法に影響はありません。It has no impact on the way HttpClient is used. 現在 HttpClient インスタンスが作成されている場所で、それを CreateClient の呼び出しに置き換えます。In places where HttpClient instances are currently created, replace those occurrences with a call to CreateClient.

名前付きクライアントNamed clients

それぞれ構成が異なる多数の HttpClient をアプリで個別に使用する必要がある場合は、 名前付きクライアント を使用することができます。If an app requires many distinct uses of HttpClient, each with a different configuration, an option is to use named clients. 名前付き HttpClient の構成は、Startup.ConfigureServices での登録時に指定できます。Configuration for a named HttpClient can be specified during registration in Startup.ConfigureServices.

services.AddHttpClient("github", c =>
{
    c.BaseAddress = new Uri("https://api.github.com/");
    // Github API versioning
    c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    // Github requires a user-agent
    c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});

上記のコードでは、 github という名前を指定して AddHttpClient を呼び出しています。In the preceding code, AddHttpClient is called, providing the name github. このクライアントには既定の構成がいくつか適用されています。つまり、GitHub API を使用するために必要なベース アドレスと 2 つのヘッダーです。This client has some default configuration applied—namely the base address and two headers required to work with the GitHub API.

CreateClient を呼び出すたびに、HttpClient の新しいインスタンスが作成されて、構成アクションが呼び出されます。Each time CreateClient is called, a new instance of HttpClient is created and the configuration action is called.

名前付きクライアントを使用するには、文字列パラメーターを CreateClient に渡すことができます。To consume a named client, a string parameter can be passed to CreateClient. 作成するクライアントの名前を指定します。Specify the name of the client to be created:

public class NamedClientModel : PageModel
{
    private readonly IHttpClientFactory _clientFactory;

    public IEnumerable<GitHubPullRequest> PullRequests { get; private set; }

    public bool GetPullRequestsError { get; private set; }

    public bool HasPullRequests => PullRequests.Any();

    public NamedClientModel(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }

    public async Task OnGet()
    {
        var request = new HttpRequestMessage(HttpMethod.Get, 
            "repos/aspnet/AspNetCore.Docs/pulls");

        var client = _clientFactory.CreateClient("github");

        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            PullRequests = await response.Content
                .ReadAsAsync<IEnumerable<GitHubPullRequest>>();
        }
        else
        {
            GetPullRequestsError = true;
            PullRequests = Array.Empty<GitHubPullRequest>();
        }
    }
}

上記のコードでは、要求でホスト名を指定する必要はありません。In the preceding code, the request doesn't need to specify a hostname. クライアントに構成されているベース アドレスが使用されるため、パスだけを渡すことができます。It can pass just the path, since the base address configured for the client is used.

型指定されたクライアントTyped clients

型指定されたクライアント:Typed clients:

  • キーとして文字列を使用する必要なしに、名前付きクライアントと同じ機能を提供します。Provide the same capabilities as named clients without the need to use strings as keys.
  • クライアントを使用するときに、IntelliSense とコンパイラのヘルプが提供されます。Provides IntelliSense and compiler help when consuming clients.
  • 特定の HttpClient を構成してそれと対話する 1 つの場所を提供します。Provide a single location to configure and interact with a particular HttpClient. たとえば、単一の型指定されたクライアントを単一のバックエンド エンドポイントに対して使用し、そのエンドポイントを操作するすべてのロジックをカプセル化できます。For example, a single typed client might be used for a single backend endpoint and encapsulate all logic dealing with that endpoint.
  • DI に対応しており、アプリ内の必要な場所に挿入できます。Work with DI and can be injected where required in your app.

型指定されたクライアントは、そのコンストラクターで HttpClient パラメーターを受け取ります。A typed client accepts an HttpClient parameter in its constructor:

public class GitHubService
{
    public HttpClient Client { get; }

    public GitHubService(HttpClient client)
    {
        client.BaseAddress = new Uri("https://api.github.com/");
        // GitHub API versioning
        client.DefaultRequestHeaders.Add("Accept", 
            "application/vnd.github.v3+json");
        // GitHub requires a user-agent
        client.DefaultRequestHeaders.Add("User-Agent", 
            "HttpClientFactory-Sample");

        Client = client;
    }

    public async Task<IEnumerable<GitHubIssue>> GetAspNetDocsIssues()
    {
        var response = await Client.GetAsync(
            "/repos/aspnet/AspNetCore.Docs/issues?state=open&sort=created&direction=desc");

        response.EnsureSuccessStatusCode();

        var result = await response.Content
            .ReadAsAsync<IEnumerable<GitHubIssue>>();

        return result;
    }
}

上記のコードでは、構成が型指定されたクライアントに移動されています。In the preceding code, the configuration is moved into the typed client. HttpClient オブジェクトは、パブリック プロパティとして公開されます。The HttpClient object is exposed as a public property. HttpClient 機能を公開する API 固有のメソッドを定義することができます。It's possible to define API-specific methods that expose HttpClient functionality. GetAspNetDocsIssues メソッドは、GitHub リポジトリで最新の未解決の問題をクエリして解析するために必要なコードをカプセル化します。The GetAspNetDocsIssues method encapsulates the code needed to query for and parse out the latest open issues from a GitHub repository.

型指定されたクライアントを登録するには、ジェネリック AddHttpClient 拡張メソッドを Startup.ConfigureServices 内で使用して、型指定されたクライアントのクラスを指定します。To register a typed client, the generic AddHttpClient extension method can be used within Startup.ConfigureServices, specifying the typed client class:

services.AddHttpClient<GitHubService>();

型指定されたクライアントは、DI で一時的として登録されます。The typed client is registered as transient with DI. 型指定されたクライアントは、直接挿入して使用できます。The typed client can be injected and consumed directly:

public class TypedClientModel : PageModel
{
    private readonly GitHubService _gitHubService;

    public IEnumerable<GitHubIssue> LatestIssues { get; private set; }

    public bool HasIssue => LatestIssues.Any();

    public bool GetIssuesError { get; private set; }

    public TypedClientModel(GitHubService gitHubService)
    {
        _gitHubService = gitHubService;
    }

    public async Task OnGet()
    {
        try
        {
            LatestIssues = await _gitHubService.GetAspNetDocsIssues();
        }
        catch(HttpRequestException)
        {
            GetIssuesError = true;
            LatestIssues = Array.Empty<GitHubIssue>();
        }
    }
}

好みに応じて、型指定されたクライアントのコンストラクターではなく、Startup.ConfigureServices での登録時に型指定されたクライアントの構成を指定できます。If preferred, the configuration for a typed client can be specified during registration in Startup.ConfigureServices, rather than in the typed client's constructor:

services.AddHttpClient<RepoService>(c =>
{
    c.BaseAddress = new Uri("https://api.github.com/");
    c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});

型指定されたクライアントの内部に HttpClient を完全にカプセル化することができます。It's possible to entirely encapsulate the HttpClient within a typed client. プロパティとして公開するのではなく、HttpClient インスタンスを内部的に呼び出すパブリック メソッドとして提供することができます。Rather than exposing it as a property, public methods can be provided which call the HttpClient instance internally.

public class RepoService
{
    // _httpClient isn't exposed publicly
    private readonly HttpClient _httpClient;

    public RepoService(HttpClient client)
    {
        _httpClient = client;
    }

    public async Task<IEnumerable<string>> GetRepos()
    {
        var response = await _httpClient.GetAsync("aspnet/repos");

        response.EnsureSuccessStatusCode();

        var result = await response.Content
            .ReadAsAsync<IEnumerable<string>>();

        return result;
    }
}

上記のコードでは、HttpClient はプライベート フィールドとして格納されます。In the preceding code, the HttpClient is stored as a private field. 外部呼び出しを行うすべてのアクセスは、GetRepos メソッドを通して行われます。All access to make external calls goes through the GetRepos method.

生成されたクライアントGenerated clients

IHttpClientFactory は、Refit などの他のサードパーティ製ライブラリと組み合わせて使用できます。IHttpClientFactory can be used in combination with other third-party libraries such as Refit. Refit は、.NET 用の REST ライブラリです。Refit is a REST library for .NET. REST API をライブ インターフェイスに変換します。It converts REST APIs into live interfaces. インターフェイスの実装は RestService によって動的に生成され、HttpClient を使用して外部 HTTP の呼び出しを行います。An implementation of the interface is generated dynamically by the RestService, using HttpClient to make the external HTTP calls.

インターフェイスと応答は、外部の API とその応答を表すように定義されます。An interface and a reply are defined to represent the external API and its response:

public interface IHelloClient
{
    [Get("/helloworld")]
    Task<Reply> GetMessageAsync();
}

public class Reply
{
    public string Message { get; set; }
}

型指定されたクライアントを追加し、Refit を使用して実装を生成できます。A typed client can be added, using Refit to generate the implementation:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient("hello", c =>
    {
        c.BaseAddress = new Uri("https://localhost:5001");
    })
    .AddTypedClient(c => Refit.RestService.For<IHelloClient>(c));

    services.AddMvc();
}

定義されたインターフェイスは、DI および Refit によって提供される実装で必要に応じて使用できます。The defined interface can be consumed where necessary, with the implementation provided by DI and Refit:

[ApiController]
public class ValuesController : ControllerBase
{
    private readonly IHelloClient _client;

    public ValuesController(IHelloClient client)
    {
        _client = client;
    }

    [HttpGet("/")]
    public async Task<ActionResult<Reply>> Index()
    {
        return await _client.GetMessageAsync();
    }
}

送信要求ミドルウェアOutgoing request middleware

HttpClient は既に、送信 HTTP 要求用にリンクできるハンドラーのデリゲートの概念を備えています。HttpClient already has the concept of delegating handlers that can be linked together for outgoing HTTP requests. IHttpClientFactory を使用すると、各名前付きクライアントに適用するハンドラーを簡単に定義できます。The IHttpClientFactory makes it easy to define the handlers to apply for each named client. 複数のハンドラーを登録してチェーン化し、送信要求ミドルウェア パイプラインを構築できます。It supports registration and chaining of multiple handlers to build an outgoing request middleware pipeline. 各ハンドラーは、送信要求の前と後に処理を実行できます。Each of these handlers is able to perform work before and after the outgoing request. このパターンは、ASP.NET Core での受信ミドルウェア パイプラインに似ています。This pattern is similar to the inbound middleware pipeline in ASP.NET Core. このパターンは、キャッシュ、エラー処理、シリアル化、ログ記録など、HTTP 要求に関する横断的関心事を管理するためのメカニズムを提供します。The pattern provides a mechanism to manage cross-cutting concerns around HTTP requests, including caching, error handling, serialization, and logging.

ハンドラーを作成するには、DelegatingHandler の派生クラスを定義します。To create a handler, define a class deriving from DelegatingHandler. パイプライン内の次のハンドラーに要求を渡す前にコードを実行するように、SendAsync メソッドをオーバーライドします。Override the SendAsync method to execute code before passing the request to the next handler in the pipeline:

public class ValidateHeaderHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        if (!request.Headers.Contains("X-API-KEY"))
        {
            return new HttpResponseMessage(HttpStatusCode.BadRequest)
            {
                Content = new StringContent(
                    "You must supply an API key header called X-API-KEY")
            };
        }

        return await base.SendAsync(request, cancellationToken);
    }
}

上記のコードでは、基本的なハンドラーが定義されています。The preceding code defines a basic handler. このコードは、X-API-KEY ヘッダーが要求に含まれていたかどうかを確認します。It checks to see if an X-API-KEY header has been included on the request. ヘッダーがない場合、HTTP 呼び出しを行わずに適切な応答を返すことができます。If the header is missing, it can avoid the HTTP call and return a suitable response.

登録時には、1 つまたは複数のハンドラーを HttpClient の構成に追加することができます。During registration, one or more handlers can be added to the configuration for an HttpClient. このタスクは、IHttpClientBuilder の拡張メソッドを使用して実行されます。This task is accomplished via extension methods on the IHttpClientBuilder.

services.AddTransient<ValidateHeaderHandler>();

services.AddHttpClient("externalservice", c =>
{
    // Assume this is an "external" service which requires an API KEY
    c.BaseAddress = new Uri("https://localhost:5000/");
})
.AddHttpMessageHandler<ValidateHeaderHandler>();

上記のコードでは、ValidateHeaderHandler が DI で登録されます。In the preceding code, the ValidateHeaderHandler is registered with DI. IHttpClientFactory では、ハンドラーごとに個別の DI スコープが作成されます。The IHttpClientFactory creates a separate DI scope for each handler. ハンドラーは、任意のスコープのサービスに自由に依存することができます。Handlers are free to depend upon services of any scope. ハンドラーが依存するサービスは、ハンドラーが破棄されるときに破棄されます。Services that handlers depend upon are disposed when the handler is disposed.

登録が済むと、AddHttpMessageHandler を呼び出してハンドラーの型を渡すことができます。Once registered, AddHttpMessageHandler can be called, passing in the type for the handler.

実行する順序で、複数のハンドラーを登録することができます。Multiple handlers can be registered in the order that they should execute. 最後の HttpClientHandler が要求を実行するまで、各ハンドラーは次のハンドラーをラップします。Each handler wraps the next handler until the final HttpClientHandler executes the request:

services.AddTransient<SecureRequestHandler>();
services.AddTransient<RequestDataHandler>();

services.AddHttpClient("clientwithhandlers")
    // This handler is on the outside and called first during the 
    // request, last during the response.
    .AddHttpMessageHandler<SecureRequestHandler>()
    // This handler is on the inside, closest to the request being 
    // sent.
    .AddHttpMessageHandler<RequestDataHandler>();

次の方法のいずれかを使って、要求ごとの状態をメッセージ ハンドラーと共有します。Use one of the following approaches to share per-request state with message handlers:

  • HttpRequestMessage.Properties を使ってデータをハンドラーに渡します。Pass data into the handler using HttpRequestMessage.Properties.
  • IHttpContextAccessor を使って現在の要求にアクセスします。Use IHttpContextAccessor to access the current request.
  • データを渡すカスタムの AsyncLocal ストレージ オブジェクトを作成します。Create a custom AsyncLocal storage object to pass the data.

Polly ベースのハンドラーを使用するUse Polly-based handlers

IHttpClientFactory は、Polly という名前の人気のあるサードパーティ製ライブラリと統合します。IHttpClientFactory integrates with a popular third-party library called Polly. Polly は、.NET 用の包括的な回復力および一時的エラー処理ライブラリです。Polly is a comprehensive resilience and transient fault-handling library for .NET. 開発者は、自然でスレッド セーフな方法を使用して、Retry、Circuit Breaker、Timeout、Bulkhead Isolation、Fallback などのポリシーを表現できます。It allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.

構成されている HttpClient インスタンスで Polly ポリシーを使用できるようにするための、拡張メソッドが提供されています。Extension methods are provided to enable the use of Polly policies with configured HttpClient instances. Polly の拡張機能は、次のようになっています。The Polly extensions:

  • クライアントへの Polly ベースのハンドラーの追加がサポートされます。Support adding Polly-based handlers to clients.
  • Microsoft.Extensions.Http.Polly NuGet パッケージのインストール後に使用できます。Can be used after installing the Microsoft.Extensions.Http.Polly NuGet package. このパッケージは、ASP.NET Core 共有フレームワークに含まれていません。The package isn't included in the ASP.NET Core shared framework.

一時的な障害を処理するHandle transient faults

外部 HTTP を呼び出す際に発生する一般的な障害のほとんどは、一時的なものです。Most common faults occur when external HTTP calls are transient. AddTransientHttpErrorPolicy という便利な拡張メソッドが含まれており、一時的なエラーを処理するためのポリシーを定義できます。A convenient extension method called AddTransientHttpErrorPolicy is included which allows a policy to be defined to handle transient errors. この拡張メソッドで構成されたポリシーは、HttpRequestException、HTTP 5xx 応答、および HTTP 408 応答を処理します。Policies configured with this extension method handle HttpRequestException, HTTP 5xx responses, and HTTP 408 responses.

AddTransientHttpErrorPolicy 拡張メソッドは、Startup.ConfigureServices 内で使用できます。The AddTransientHttpErrorPolicy extension can be used within Startup.ConfigureServices. この拡張メソッドは、可能性のある一時的障害を表すエラーを処理するように構成された PolicyBuilder オブジェクトへのアクセスを提供します。The extension provides access to a PolicyBuilder object configured to handle errors representing a possible transient fault:

services.AddHttpClient<UnreliableEndpointCallerService>()
    .AddTransientHttpErrorPolicy(p => 
        p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));

上記のコードでは、WaitAndRetryAsync ポリシーが定義されています。In the preceding code, a WaitAndRetryAsync policy is defined. 失敗した要求は最大 3 回再試行され、再試行の間には 600 ミリ秒の遅延が設けられます。Failed requests are retried up to three times with a delay of 600 ms between attempts.

ポリシーを動的に選択するDynamically select policies

Polly ベースのハンドラーを追加するために使用できる追加の拡張メソッドが存在します。Additional extension methods exist which can be used to add Polly-based handlers. そのような拡張メソッドの 1 つは AddPolicyHandler であり、複数のオーバーロードを持ちます。One such extension is AddPolicyHandler, which has multiple overloads. 1 つのオーバーロードでは、適用するポリシーを定義するときに要求を検査できます。One overload allows the request to be inspected when defining which policy to apply:

var timeout = Policy.TimeoutAsync<HttpResponseMessage>(
    TimeSpan.FromSeconds(10));
var longTimeout = Policy.TimeoutAsync<HttpResponseMessage>(
    TimeSpan.FromSeconds(30));

services.AddHttpClient("conditionalpolicy")
// Run some code to select a policy based on the request
    .AddPolicyHandler(request => 
        request.Method == HttpMethod.Get ? timeout : longTimeout);

上記のコードでは、送信要求が HTTP GET の場合は、10 秒のタイムアウトが適用されます。In the preceding code, if the outgoing request is an HTTP GET, a 10-second timeout is applied. 他の HTTP メソッドの場合は、30 秒のタイムアウトが使用されます。For any other HTTP method, a 30-second timeout is used.

複数の Polly ハンドラーを追加するAdd multiple Polly handlers

Polly ポリシーを入れ子にして拡張機能を提供するのが一般的です。It's common to nest Polly policies to provide enhanced functionality:

services.AddHttpClient("multiplepolicies")
    .AddTransientHttpErrorPolicy(p => p.RetryAsync(3))
    .AddTransientHttpErrorPolicy(
        p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));

前の例では、2 つのハンドラーが追加されています。In the preceding example, two handlers are added. 最初のハンドラーは、AddTransientHttpErrorPolicy 拡張メソッドを使用して再試行ポリシーを追加します。The first uses the AddTransientHttpErrorPolicy extension to add a retry policy. 失敗した要求は 3 回まで再試行されます。Failed requests are retried up to three times. AddTransientHttpErrorPolicy の 2 番目の呼び出しでは、サーキット ブレーカー ポリシーが追加されています。The second call to AddTransientHttpErrorPolicy adds a circuit breaker policy. 試行が連続して 5 回失敗した場合、それ以上の外部要求は 30 秒間ブロックされます。Further external requests are blocked for 30 seconds if five failed attempts occur sequentially. サーキット ブレーカー ポリシーはステートフルです。Circuit breaker policies are stateful. このクライアントからのすべての呼び出しは、同じサーキット状態を共有します。All calls through this client share the same circuit state.

Polly レジストリからポリシーを追加するAdd policies from the Polly registry

定期的に使用されるポリシーを管理するには、ポリシーを 1 回定義した後、PolicyRegistry でポリシーを登録します。An approach to managing regularly used policies is to define them once and register them with a PolicyRegistry. 提供されている拡張メソッドを使用することで、レジストリからポリシーを使用してハンドラーを追加できます。An extension method is provided which allows a handler to be added using a policy from the registry:

var registry = services.AddPolicyRegistry();

registry.Add("regular", timeout);
registry.Add("long", longTimeout);

services.AddHttpClient("regulartimeouthandler")
    .AddPolicyHandlerFromRegistry("regular");

上記のコードでは、PolicyRegistryServiceCollection に追加されるときに 2 つのポリシーが登録されています。In the preceding code, two policies are registered when the PolicyRegistry is added to the ServiceCollection. レジストリからポリシーを使用するには、適用するポリシーの名前を渡して AddPolicyHandlerFromRegistry メソッドを使用します。To use a policy from the registry, the AddPolicyHandlerFromRegistry method is used, passing the name of the policy to apply.

IHttpClientFactory および Polly の統合について詳しくは、Polly の Wiki をご覧ください。Further information about IHttpClientFactory and Polly integrations can be found on the Polly wiki.

HttpClient と有効期間の管理HttpClient and lifetime management

IHttpClientFactoryCreateClient を呼び出すたびに、HttpClient の新しいインスタンスが返されます。A new HttpClient instance is returned each time CreateClient is called on the IHttpClientFactory. 名前付きクライアントごとに HttpMessageHandler が存在します。There's an HttpMessageHandler per named client. ファクトリによって HttpMessageHandler インスタンスの有効期間が管理されます。The factory manages the lifetimes of the HttpMessageHandler instances.

IHttpClientFactory は、リソースの消費量を減らすため、ファクトリによって作成された HttpMessageHandler のインスタンスをプールします。IHttpClientFactory pools the HttpMessageHandler instances created by the factory to reduce resource consumption. 新しい HttpClient インスタンスを作成するときに、プールの HttpMessageHandler インスタンスの有効期間が切れていない場合はそれを再利用する場合があります。An HttpMessageHandler instance may be reused from the pool when creating a new HttpClient instance if its lifetime hasn't expired.

通常、各ハンドラーでは基になる HTTP 接続が独自に管理されるため、ハンドラーはプールすることが望まれます。Pooling of handlers is desirable as each handler typically manages its own underlying HTTP connections. 必要以上のハンドラーを作成すると、接続に遅延が発生する可能性があります。Creating more handlers than necessary can result in connection delays. また、一部のハンドラーは接続を無期限に開いており、DNS の変更にハンドラーが対応できないことがあります。Some handlers also keep connections open indefinitely, which can prevent the handler from reacting to DNS changes.

ハンドラーの既定の有効期間は 2 分です。The default handler lifetime is two minutes. 名前付きクライアントごとに、既定値をオーバーライドすることができます。The default value can be overridden on a per named client basis. オーバーライドするには、クライアント作成時に返された IHttpClientBuilderSetHandlerLifetime を呼び出します。To override it, call SetHandlerLifetime on the IHttpClientBuilder that is returned when creating the client:

services.AddHttpClient("extendedhandlerlifetime")
    .SetHandlerLifetime(TimeSpan.FromMinutes(5));

クライアントを破棄する必要はありません。Disposal of the client isn't required. 破棄すると送信要求がキャンセルされ、Dispose の呼び出し後には指定された HttpClient インスタンスを使用できなくなります。Disposal cancels outgoing requests and guarantees the given HttpClient instance can't be used after calling Dispose. IHttpClientFactory は、HttpClient インスタンスによって使用されたリソースの追跡と破棄を行います。IHttpClientFactory tracks and disposes resources used by HttpClient instances. 通常、HttpClient インスタンスは、破棄を必要としない .NET オブジェクトとして扱うことができます。The HttpClient instances can generally be treated as .NET objects not requiring disposal.

IHttpClientFactory の登場以前は、1 つの HttpClient インスタンスを長い期間使い続けるパターンが一般的に使用されていました。Keeping a single HttpClient instance alive for a long duration is a common pattern used before the inception of IHttpClientFactory. IHttpClientFactory に移行した後は、このパターンは不要になります。This pattern becomes unnecessary after migrating to IHttpClientFactory.

IHttpClientFactory の代替手段Alternatives to IHttpClientFactory

DI 対応のアプリ内で IHttpClientFactory を使用すれば、次のことを回避できます。Using IHttpClientFactory in a DI-enabled app avoids:

  • HttpMessageHandler インスタンスをプールすることによるリソース枯渇の問題。Resource exhaustion problems by pooling HttpMessageHandler instances.
  • 一定の間隔で HttpMessageHandler インスタンスを循環させることによって発生する古くなった DNS の問題。Stale DNS problems by cycling HttpMessageHandler instances at regular intervals.

有効期間の長い SocketsHttpHandler インスタンスを使用して、上記の問題を解決する別の方法があります。There are alternative ways to solve the preceding problems using a long-lived SocketsHttpHandler instance.

  • アプリの起動時に SocketsHttpHandler インスタンスを作成し、アプリの有効期間中、それを使用します。Create an instance of SocketsHttpHandler when the app starts and use it for the life of the app.
  • DNS の更新時間に基づいて、PooledConnectionLifetime を適切な値に構成します。Configure PooledConnectionLifetime to an appropriate value based on DNS refresh times.
  • 必要に応じて new HttpClient(handler, disposeHandler: false) を使用して HttpClient インスタンスを作成します。Create HttpClient instances using new HttpClient(handler, disposeHandler: false) as needed.

上記の方法を使用すると、IHttpClientFactory が同様の方法で解決するリソース管理の問題を解決できます。The preceding approaches solve the resource management problems that IHttpClientFactory solves in a similar way.

  • SocketsHttpHandler を使用すると、HttpClient インスタンス間で接続を共有できます。The SocketsHttpHandler shares connections across HttpClient instances. この共有によってソケットの枯渇が防止されます。This sharing prevents socket exhaustion.
  • SocketsHttpHandler では、古くなった DNS の問題を回避するために PooledConnectionLifetime に従って接続を循環されます。The SocketsHttpHandler cycles connections according to PooledConnectionLifetime to avoid stale DNS problems.

CookiesCookies

HttpMessageHandler インスタンスをプールすると、CookieContainer オブジェクトが共有されます。The pooled HttpMessageHandler instances results in CookieContainer objects being shared. 予期せぬ CookieContainer オブジェクト共有があると、多くの場合、コードは不適切なものとなります。Unanticipated CookieContainer object sharing often results in incorrect code. cookie を必要とするアプリの場合は、次のいずれかを検討してください。For apps that require cookies, consider either:

  • 自動的な cookie 処理の無効化Disabling automatic cookie handling
  • IHttpClientFactory の回避Avoiding IHttpClientFactory

ConfigurePrimaryHttpMessageHandler を呼び出して、自動的な cookie 処理を無効にします。Call ConfigurePrimaryHttpMessageHandler to disable automatic cookie handling:

services.AddHttpClient("configured-disable-automatic-cookies")
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        return new SocketsHttpHandler()
        {
            UseCookies = false,
        };
    });

ログの記録Logging

IHttpClientFactory によって作成されたクライアントは、すべての要求のログ メッセージを記録します。Clients created via IHttpClientFactory record log messages for all requests. 既定のログ メッセージを見るには、ログの構成で適切な情報レベルを有効にします。Enable the appropriate information level in your logging configuration to see the default log messages. 要求ヘッダーのログなどの追加ログは、トレース レベルでのみ含まれます。Additional logging, such as the logging of request headers, is only included at trace level.

各クライアントに使用されるログのカテゴリには、クライアントの名前が含まれます。The log category used for each client includes the name of the client. たとえば、 MyNamedClient という名前のクライアントは、カテゴリ System.Net.Http.HttpClient.MyNamedClient.LogicalHandler でメッセージをログに記録します。A client named MyNamedClient , for example, logs messages with a category of System.Net.Http.HttpClient.MyNamedClient.LogicalHandler. LogicalHandler というサフィックスが付いたメッセージが、要求ハンドラーのパイプラインの外部で発生します。Messages suffixed with LogicalHandler occur outside the request handler pipeline. 要求では、パイプライン内の他のハンドラーが要求を処理する前に、メッセージがログに記録されます。On the request, messages are logged before any other handlers in the pipeline have processed it. 応答では、他のパイプライン ハンドラーが応答を受け取った後で、メッセージがログに記録されます。On the response, messages are logged after any other pipeline handlers have received the response.

ログ記録は、要求ハンドラーのパイプラインの内部でも行われます。Logging also occurs inside the request handler pipeline. MyNamedClient の例では、それらのメッセージはログ カテゴリ System.Net.Http.HttpClient.MyNamedClient.ClientHandler に対して記録されます。In the MyNamedClient example, those messages are logged against the log category System.Net.Http.HttpClient.MyNamedClient.ClientHandler. 要求では、他のすべてのハンドラーが実行した後、要求がネットワークに送信される直前に、これが行われます。For the request, this occurs after all other handlers have run and immediately before the request is sent out on the network. 応答では、このログ記録には、ハンドラー パイプラインを通じ戻される前の応答の状態が含まれます。On the response, this logging includes the state of the response before it passes back through the handler pipeline.

パイプラインの外部と内部でログを有効にすると、他のパイプライン ハンドラーによる変更を検査できます。Enabling logging outside and inside the pipeline enables inspection of the changes made by the other pipeline handlers. これには、たとえば、要求ヘッダーや応答状態コードへの変更を含めることができます。This may include changes to request headers, for example, or to the response status code.

ログのカテゴリにクライアントの名前を含めると、必要に応じて、特定の名前付きクライアントでログをフィルター処理できます。Including the name of the client in the log category enables log filtering for specific named clients where necessary.

HttpMessageHandler を構成するConfigure the HttpMessageHandler

クライアントによって使用される内部 HttpMessageHandler の構成を制御することが必要な場合があります。It may be necessary to control the configuration of the inner HttpMessageHandler used by a client.

名前付きクライアントまたは型指定されたクライアントを追加すると、IHttpClientBuilder が返されます。An IHttpClientBuilder is returned when adding named or typed clients. ConfigurePrimaryHttpMessageHandler 拡張メソッドを使用して、デリゲートを定義することができます。The ConfigurePrimaryHttpMessageHandler extension method can be used to define a delegate. デリゲートは、そのクライアントによって使用されるプライマリ HttpMessageHandler の作成と構成に使用されます。The delegate is used to create and configure the primary HttpMessageHandler used by that client:

services.AddHttpClient("configured-inner-handler")
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        return new HttpClientHandler()
        {
            AllowAutoRedirect = false,
            UseDefaultCredentials = true
        };
    });

コンソール アプリで IHttpClientFactory を使用するUse IHttpClientFactory in a console app

コンソール アプリで、次のパッケージ参照をプロジェクトに追加します。In a console app, add the following package references to the project:

次に例を示します。In the following example:

  • IHttpClientFactory汎用ホストのサービス コンテナーに登録されます。IHttpClientFactory is registered in the Generic Host's service container.
  • MyService により、クライアント ファクトリ インスタンスがサービスから作成され、それが HttpClient の作成に使用されます。MyService creates a client factory instance from the service, which is used to create an HttpClient. HttpClient は Web ページを取得する目的で使用されます。HttpClient is used to retrieve a webpage.
  • Main により、サービスの GetPage メソッドを実行し、Web ページ コンテンツの最初の 500 文字をコンソールに書き込むためのスコープが作成されます。Main creates a scope to execute the service's GetPage method and write the first 500 characters of the webpage content to the console.
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
class Program
{
    static async Task<int> Main(string[] args)
    {
        var builder = new HostBuilder()
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHttpClient();
                services.AddTransient<IMyService, MyService>();
            }).UseConsoleLifetime();

        var host = builder.Build();

        using (var serviceScope = host.Services.CreateScope())
        {
            var services = serviceScope.ServiceProvider;

            try
            {
                var myService = services.GetRequiredService<IMyService>();
                var pageContent = await myService.GetPage();

                Console.WriteLine(pageContent.Substring(0, 500));
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();

                logger.LogError(ex, "An error occurred.");
            }
        }

        return 0;
    }

    public interface IMyService
    {
        Task<string> GetPage();
    }

    public class MyService : IMyService
    {
        private readonly IHttpClientFactory _clientFactory;

        public MyService(IHttpClientFactory clientFactory)
        {
            _clientFactory = clientFactory;
        }

        public async Task<string> GetPage()
        {
            // Content from BBC One: Dr. Who website (©BBC)
            var request = new HttpRequestMessage(HttpMethod.Get,
                "https://www.bbc.co.uk/programmes/b006q2x0");
            var client = _clientFactory.CreateClient();
            var response = await client.SendAsync(request);

            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadAsStringAsync();
            }
            else
            {
                return $"StatusCode: {response.StatusCode}";
            }
        }
    }
}

その他の技術情報Additional resources

寄稿者: Glenn CondronRyan NowakSteve GordonBy Glenn Condron, Ryan Nowak, and Steve Gordon

アプリ内で HttpClient インスタンスを構成して作成するために、IHttpClientFactory を登録して使用できます。An IHttpClientFactory can be registered and used to configure and create HttpClient instances in an app. 次のような利点があります。It offers the following benefits:

  • 論理 HttpClient インスタンスの名前付けと構成を一元化します。Provides a central location for naming and configuring logical HttpClient instances. たとえば、 github クライアントを登録して、 GitHub にアクセスするように構成できます。For example, a github client can be registered and configured to access GitHub. 既定のクライアントは、他の目的に登録できます。A default client can be registered for other purposes.
  • HttpClient でのハンドラーのデリゲートにより送信ミドルウェアの概念を体系化し、Polly ベースのミドルウェアでそれを利用するための拡張機能を提供します。Codifies the concept of outgoing middleware via delegating handlers in HttpClient and provides extensions for Polly-based middleware to take advantage of that.
  • 基になっている HttpClientMessageHandler インスタンスのプールと有効期間を管理し、HttpClient の有効期間を手動で管理するときに発生する一般的な DNS の問題を防ぎます。Manages the pooling and lifetime of underlying HttpClientMessageHandler instances to avoid common DNS problems that occur when manually managing HttpClient lifetimes.
  • ファクトリによって作成されたクライアントから送信されるすべての要求に対し、(ILogger によって) 構成可能なログ エクスペリエンスを追加します。Adds a configurable logging experience (via ILogger) for all requests sent through clients created by the factory.

サンプル コードを表示またはダウンロードします (ダウンロード方法)。View or download sample code (how to download)

必須コンポーネントPrerequisites

.NET Framework をターゲットとするプロジェクトでは、Microsoft.Extensions.Http NuGet パッケージのインストールが必要です。Projects targeting .NET Framework require installation of the Microsoft.Extensions.Http NuGet package. .NET Core をターゲットとし、Microsoft.AspNetCore.App メタパッケージを参照するプロジェクトには、既に Microsoft.Extensions.Http パッケージが含まれています。Projects that target .NET Core and reference the Microsoft.AspNetCore.App metapackage already include the Microsoft.Extensions.Http package.

利用パターンConsumption patterns

アプリで IHttpClientFactory を使用するには複数の方法があります。There are several ways IHttpClientFactory can be used in an app:

これらの間に厳密な優劣はありません。None of them are strictly superior to another. どの方法が最善かは、アプリの制約に依存します。The best approach depends upon the app's constraints.

基本的な使用方法Basic usage

IHttpClientFactory は、Startup.ConfigureServices メソッドの内部で IServiceCollectionAddHttpClient 拡張メソッドを呼び出すことによって登録できます。The IHttpClientFactory can be registered by calling the AddHttpClient extension method on the IServiceCollection, inside the Startup.ConfigureServices method.

services.AddHttpClient();

登録が済むと、コードは、依存関係の挿入 (DI) を使用してサービスを挿入できる任意の場所で、IHttpClientFactory を受け取ることができます。Once registered, code can accept an IHttpClientFactory anywhere services can be injected with dependency injection (DI). IHttpClientFactory を使用して、HttpClient インスタンスを作成できます。The IHttpClientFactory can be used to create an HttpClient instance:

public class BasicUsageModel : PageModel
{
    private readonly IHttpClientFactory _clientFactory;

    public IEnumerable<GitHubBranch> Branches { get; private set; }

    public bool GetBranchesError { get; private set; }

    public BasicUsageModel(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }

    public async Task OnGet()
    {
        var request = new HttpRequestMessage(HttpMethod.Get, 
            "https://api.github.com/repos/aspnet/AspNetCore.Docs/branches");
        request.Headers.Add("Accept", "application/vnd.github.v3+json");
        request.Headers.Add("User-Agent", "HttpClientFactory-Sample");

        var client = _clientFactory.CreateClient();

        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            Branches = await response.Content
                .ReadAsAsync<IEnumerable<GitHubBranch>>();
        }
        else
        {
            GetBranchesError = true;
            Branches = Array.Empty<GitHubBranch>();
        }                               
    }
}

このように IHttpClientFactory を使用するのは、既存のアプリをリファクタリングする優れた方法です。Using IHttpClientFactory in this fashion is a good way to refactor an existing app. HttpClient の使用方法に影響はありません。It has no impact on the way HttpClient is used. 現在 HttpClient インスタンスが作成されている場所で、それを CreateClient の呼び出しに置き換えます。In places where HttpClient instances are currently created, replace those occurrences with a call to CreateClient.

名前付きクライアントNamed clients

それぞれ構成が異なる多数の HttpClient をアプリで個別に使用する必要がある場合は、 名前付きクライアント を使用することができます。If an app requires many distinct uses of HttpClient, each with a different configuration, an option is to use named clients. 名前付き HttpClient の構成は、Startup.ConfigureServices での登録時に指定できます。Configuration for a named HttpClient can be specified during registration in Startup.ConfigureServices.

services.AddHttpClient("github", c =>
{
    c.BaseAddress = new Uri("https://api.github.com/");
    // Github API versioning
    c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    // Github requires a user-agent
    c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});

上記のコードでは、 github という名前を指定して AddHttpClient を呼び出しています。In the preceding code, AddHttpClient is called, providing the name github. このクライアントには既定の構成がいくつか適用されています。つまり、GitHub API を使用するために必要なベース アドレスと 2 つのヘッダーです。This client has some default configuration applied—namely the base address and two headers required to work with the GitHub API.

CreateClient を呼び出すたびに、HttpClient の新しいインスタンスが作成されて、構成アクションが呼び出されます。Each time CreateClient is called, a new instance of HttpClient is created and the configuration action is called.

名前付きクライアントを使用するには、文字列パラメーターを CreateClient に渡すことができます。To consume a named client, a string parameter can be passed to CreateClient. 作成するクライアントの名前を指定します。Specify the name of the client to be created:

public class NamedClientModel : PageModel
{
    private readonly IHttpClientFactory _clientFactory;

    public IEnumerable<GitHubPullRequest> PullRequests { get; private set; }

    public bool GetPullRequestsError { get; private set; }

    public bool HasPullRequests => PullRequests.Any();

    public NamedClientModel(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }

    public async Task OnGet()
    {
        var request = new HttpRequestMessage(HttpMethod.Get, 
            "repos/aspnet/AspNetCore.Docs/pulls");

        var client = _clientFactory.CreateClient("github");

        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            PullRequests = await response.Content
                .ReadAsAsync<IEnumerable<GitHubPullRequest>>();
        }
        else
        {
            GetPullRequestsError = true;
            PullRequests = Array.Empty<GitHubPullRequest>();
        }
    }
}

上記のコードでは、要求でホスト名を指定する必要はありません。In the preceding code, the request doesn't need to specify a hostname. クライアントに構成されているベース アドレスが使用されるため、パスだけを渡すことができます。It can pass just the path, since the base address configured for the client is used.

型指定されたクライアントTyped clients

型指定されたクライアント:Typed clients:

  • キーとして文字列を使用する必要なしに、名前付きクライアントと同じ機能を提供します。Provide the same capabilities as named clients without the need to use strings as keys.
  • クライアントを使用するときに、IntelliSense とコンパイラのヘルプが提供されます。Provides IntelliSense and compiler help when consuming clients.
  • 特定の HttpClient を構成してそれと対話する 1 つの場所を提供します。Provide a single location to configure and interact with a particular HttpClient. たとえば、単一の型指定されたクライアントを単一のバックエンド エンドポイントに対して使用し、そのエンドポイントを操作するすべてのロジックをカプセル化できます。For example, a single typed client might be used for a single backend endpoint and encapsulate all logic dealing with that endpoint.
  • DI に対応しており、アプリ内の必要な場所に挿入できます。Work with DI and can be injected where required in your app.

型指定されたクライアントは、そのコンストラクターで HttpClient パラメーターを受け取ります。A typed client accepts an HttpClient parameter in its constructor:

public class GitHubService
{
    public HttpClient Client { get; }

    public GitHubService(HttpClient client)
    {
        client.BaseAddress = new Uri("https://api.github.com/");
        // GitHub API versioning
        client.DefaultRequestHeaders.Add("Accept", 
            "application/vnd.github.v3+json");
        // GitHub requires a user-agent
        client.DefaultRequestHeaders.Add("User-Agent", 
            "HttpClientFactory-Sample");

        Client = client;
    }

    public async Task<IEnumerable<GitHubIssue>> GetAspNetDocsIssues()
    {
        var response = await Client.GetAsync(
            "/repos/aspnet/AspNetCore.Docs/issues?state=open&sort=created&direction=desc");

        response.EnsureSuccessStatusCode();

        var result = await response.Content
            .ReadAsAsync<IEnumerable<GitHubIssue>>();

        return result;
    }
}

上記のコードでは、構成が型指定されたクライアントに移動されています。In the preceding code, the configuration is moved into the typed client. HttpClient オブジェクトは、パブリック プロパティとして公開されます。The HttpClient object is exposed as a public property. HttpClient 機能を公開する API 固有のメソッドを定義することができます。It's possible to define API-specific methods that expose HttpClient functionality. GetAspNetDocsIssues メソッドは、GitHub リポジトリで最新の未解決の問題をクエリして解析するために必要なコードをカプセル化します。The GetAspNetDocsIssues method encapsulates the code needed to query for and parse out the latest open issues from a GitHub repository.

型指定されたクライアントを登録するには、ジェネリック AddHttpClient 拡張メソッドを Startup.ConfigureServices 内で使用して、型指定されたクライアントのクラスを指定します。To register a typed client, the generic AddHttpClient extension method can be used within Startup.ConfigureServices, specifying the typed client class:

services.AddHttpClient<GitHubService>();

型指定されたクライアントは、DI で一時的として登録されます。The typed client is registered as transient with DI. 型指定されたクライアントは、直接挿入して使用できます。The typed client can be injected and consumed directly:

public class TypedClientModel : PageModel
{
    private readonly GitHubService _gitHubService;

    public IEnumerable<GitHubIssue> LatestIssues { get; private set; }

    public bool HasIssue => LatestIssues.Any();

    public bool GetIssuesError { get; private set; }

    public TypedClientModel(GitHubService gitHubService)
    {
        _gitHubService = gitHubService;
    }

    public async Task OnGet()
    {
        try
        {
            LatestIssues = await _gitHubService.GetAspNetDocsIssues();
        }
        catch(HttpRequestException)
        {
            GetIssuesError = true;
            LatestIssues = Array.Empty<GitHubIssue>();
        }
    }
}

好みに応じて、型指定されたクライアントのコンストラクターではなく、Startup.ConfigureServices での登録時に型指定されたクライアントの構成を指定できます。If preferred, the configuration for a typed client can be specified during registration in Startup.ConfigureServices, rather than in the typed client's constructor:

services.AddHttpClient<RepoService>(c =>
{
    c.BaseAddress = new Uri("https://api.github.com/");
    c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});

型指定されたクライアントの内部に HttpClient を完全にカプセル化することができます。It's possible to entirely encapsulate the HttpClient within a typed client. プロパティとして公開するのではなく、HttpClient インスタンスを内部的に呼び出すパブリック メソッドとして提供することができます。Rather than exposing it as a property, public methods can be provided which call the HttpClient instance internally.

public class RepoService
{
    // _httpClient isn't exposed publicly
    private readonly HttpClient _httpClient;

    public RepoService(HttpClient client)
    {
        _httpClient = client;
    }

    public async Task<IEnumerable<string>> GetRepos()
    {
        var response = await _httpClient.GetAsync("aspnet/repos");

        response.EnsureSuccessStatusCode();

        var result = await response.Content
            .ReadAsAsync<IEnumerable<string>>();

        return result;
    }
}

上記のコードでは、HttpClient はプライベート フィールドとして格納されます。In the preceding code, the HttpClient is stored as a private field. 外部呼び出しを行うすべてのアクセスは、GetRepos メソッドを通して行われます。All access to make external calls goes through the GetRepos method.

生成されたクライアントGenerated clients

IHttpClientFactory は、Refit などの他のサードパーティ製ライブラリと組み合わせて使用できます。IHttpClientFactory can be used in combination with other third-party libraries such as Refit. Refit は、.NET 用の REST ライブラリです。Refit is a REST library for .NET. REST API をライブ インターフェイスに変換します。It converts REST APIs into live interfaces. インターフェイスの実装は RestService によって動的に生成され、HttpClient を使用して外部 HTTP の呼び出しを行います。An implementation of the interface is generated dynamically by the RestService, using HttpClient to make the external HTTP calls.

インターフェイスと応答は、外部の API とその応答を表すように定義されます。An interface and a reply are defined to represent the external API and its response:

public interface IHelloClient
{
    [Get("/helloworld")]
    Task<Reply> GetMessageAsync();
}

public class Reply
{
    public string Message { get; set; }
}

型指定されたクライアントを追加し、Refit を使用して実装を生成できます。A typed client can be added, using Refit to generate the implementation:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient("hello", c =>
    {
        c.BaseAddress = new Uri("http://localhost:5000");
    })
    .AddTypedClient(c => Refit.RestService.For<IHelloClient>(c));

    services.AddMvc();
}

定義されたインターフェイスは、DI および Refit によって提供される実装で必要に応じて使用できます。The defined interface can be consumed where necessary, with the implementation provided by DI and Refit:

[ApiController]
public class ValuesController : ControllerBase
{
    private readonly IHelloClient _client;

    public ValuesController(IHelloClient client)
    {
        _client = client;
    }

    [HttpGet("/")]
    public async Task<ActionResult<Reply>> Index()
    {
        return await _client.GetMessageAsync();
    }
}

送信要求ミドルウェアOutgoing request middleware

HttpClient は既に、送信 HTTP 要求用にリンクできるハンドラーのデリゲートの概念を備えています。HttpClient already has the concept of delegating handlers that can be linked together for outgoing HTTP requests. IHttpClientFactory を使用すると、各名前付きクライアントに適用するハンドラーを簡単に定義できます。The IHttpClientFactory makes it easy to define the handlers to apply for each named client. 複数のハンドラーを登録してチェーン化し、送信要求ミドルウェア パイプラインを構築できます。It supports registration and chaining of multiple handlers to build an outgoing request middleware pipeline. 各ハンドラーは、送信要求の前と後に処理を実行できます。Each of these handlers is able to perform work before and after the outgoing request. このパターンは、ASP.NET Core での受信ミドルウェア パイプラインに似ています。This pattern is similar to the inbound middleware pipeline in ASP.NET Core. このパターンは、キャッシュ、エラー処理、シリアル化、ログ記録など、HTTP 要求に関する横断的関心事を管理するためのメカニズムを提供します。The pattern provides a mechanism to manage cross-cutting concerns around HTTP requests, including caching, error handling, serialization, and logging.

ハンドラーを作成するには、DelegatingHandler の派生クラスを定義します。To create a handler, define a class deriving from DelegatingHandler. パイプライン内の次のハンドラーに要求を渡す前にコードを実行するように、SendAsync メソッドをオーバーライドします。Override the SendAsync method to execute code before passing the request to the next handler in the pipeline:

public class ValidateHeaderHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        if (!request.Headers.Contains("X-API-KEY"))
        {
            return new HttpResponseMessage(HttpStatusCode.BadRequest)
            {
                Content = new StringContent(
                    "You must supply an API key header called X-API-KEY")
            };
        }

        return await base.SendAsync(request, cancellationToken);
    }
}

上記のコードでは、基本的なハンドラーが定義されています。The preceding code defines a basic handler. このコードは、X-API-KEY ヘッダーが要求に含まれていたかどうかを確認します。It checks to see if an X-API-KEY header has been included on the request. ヘッダーがない場合、HTTP 呼び出しを行わずに適切な応答を返すことができます。If the header is missing, it can avoid the HTTP call and return a suitable response.

登録時には、1 つまたは複数のハンドラーを HttpClient の構成に追加することができます。During registration, one or more handlers can be added to the configuration for an HttpClient. このタスクは、IHttpClientBuilder の拡張メソッドを使用して実行されます。This task is accomplished via extension methods on the IHttpClientBuilder.

services.AddTransient<ValidateHeaderHandler>();

services.AddHttpClient("externalservice", c =>
{
    // Assume this is an "external" service which requires an API KEY
    c.BaseAddress = new Uri("https://localhost:5000/");
})
.AddHttpMessageHandler<ValidateHeaderHandler>();

上記のコードでは、ValidateHeaderHandler が DI で登録されます。In the preceding code, the ValidateHeaderHandler is registered with DI. ハンドラーは、スコープではなく、一時的なサービスとして DI で登録する 必要がありますThe handler must be registered in DI as a transient service, never scoped. ハンドラーがスコープ付きサービスとして登録されており、ハンドラーが依存するサービスを破棄できる場合:If the handler is registered as a scoped service and any services that the handler depends upon are disposable:

  • ハンドラーのサービスは、ハンドラーがスコープ外に出る前に破棄されることがあります。The handler's services could be disposed before the handler goes out of scope.
  • 破棄されたハンドラー サービスが原因でハンドラーが失敗します。The disposed handler services causes the handler to fail.

登録が済むと、AddHttpMessageHandler を呼び出してハンドラーの型を渡すことができます。Once registered, AddHttpMessageHandler can be called, passing in the handler type.

実行する順序で、複数のハンドラーを登録することができます。Multiple handlers can be registered in the order that they should execute. 最後の HttpClientHandler が要求を実行するまで、各ハンドラーは次のハンドラーをラップします。Each handler wraps the next handler until the final HttpClientHandler executes the request:

services.AddTransient<SecureRequestHandler>();
services.AddTransient<RequestDataHandler>();

services.AddHttpClient("clientwithhandlers")
    // This handler is on the outside and called first during the 
    // request, last during the response.
    .AddHttpMessageHandler<SecureRequestHandler>()
    // This handler is on the inside, closest to the request being 
    // sent.
    .AddHttpMessageHandler<RequestDataHandler>();

次の方法のいずれかを使って、要求ごとの状態をメッセージ ハンドラーと共有します。Use one of the following approaches to share per-request state with message handlers:

  • HttpRequestMessage.Properties を使ってデータをハンドラーに渡します。Pass data into the handler using HttpRequestMessage.Properties.
  • IHttpContextAccessor を使って現在の要求にアクセスします。Use IHttpContextAccessor to access the current request.
  • データを渡すカスタムの AsyncLocal ストレージ オブジェクトを作成します。Create a custom AsyncLocal storage object to pass the data.

Polly ベースのハンドラーを使用するUse Polly-based handlers

IHttpClientFactory は、Polly という名前の人気のあるサードパーティ製ライブラリと統合します。IHttpClientFactory integrates with a popular third-party library called Polly. Polly は、.NET 用の包括的な回復力および一時的エラー処理ライブラリです。Polly is a comprehensive resilience and transient fault-handling library for .NET. 開発者は、自然でスレッド セーフな方法を使用して、Retry、Circuit Breaker、Timeout、Bulkhead Isolation、Fallback などのポリシーを表現できます。It allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.

構成されている HttpClient インスタンスで Polly ポリシーを使用できるようにするための、拡張メソッドが提供されています。Extension methods are provided to enable the use of Polly policies with configured HttpClient instances. Polly の拡張機能は、次のようになっています。The Polly extensions:

  • クライアントへの Polly ベースのハンドラーの追加がサポートされます。Support adding Polly-based handlers to clients.
  • Microsoft.Extensions.Http.Polly NuGet パッケージのインストール後に使用できます。Can be used after installing the Microsoft.Extensions.Http.Polly NuGet package. このパッケージは、ASP.NET Core 共有フレームワークに含まれていません。The package isn't included in the ASP.NET Core shared framework.

一時的な障害を処理するHandle transient faults

外部 HTTP を呼び出す際に発生する一般的な障害のほとんどは、一時的なものです。Most common faults occur when external HTTP calls are transient. AddTransientHttpErrorPolicy という便利な拡張メソッドが含まれており、一時的なエラーを処理するためのポリシーを定義できます。A convenient extension method called AddTransientHttpErrorPolicy is included which allows a policy to be defined to handle transient errors. この拡張メソッドで構成されたポリシーは、HttpRequestException、HTTP 5xx 応答、および HTTP 408 応答を処理します。Policies configured with this extension method handle HttpRequestException, HTTP 5xx responses, and HTTP 408 responses.

AddTransientHttpErrorPolicy 拡張メソッドは、Startup.ConfigureServices 内で使用できます。The AddTransientHttpErrorPolicy extension can be used within Startup.ConfigureServices. この拡張メソッドは、可能性のある一時的障害を表すエラーを処理するように構成された PolicyBuilder オブジェクトへのアクセスを提供します。The extension provides access to a PolicyBuilder object configured to handle errors representing a possible transient fault:

services.AddHttpClient<UnreliableEndpointCallerService>()
    .AddTransientHttpErrorPolicy(p => 
        p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));

上記のコードでは、WaitAndRetryAsync ポリシーが定義されています。In the preceding code, a WaitAndRetryAsync policy is defined. 失敗した要求は最大 3 回再試行され、再試行の間には 600 ミリ秒の遅延が設けられます。Failed requests are retried up to three times with a delay of 600 ms between attempts.

ポリシーを動的に選択するDynamically select policies

Polly ベースのハンドラーを追加するために使用できる追加の拡張メソッドが存在します。Additional extension methods exist which can be used to add Polly-based handlers. そのような拡張メソッドの 1 つは AddPolicyHandler であり、複数のオーバーロードを持ちます。One such extension is AddPolicyHandler, which has multiple overloads. 1 つのオーバーロードでは、適用するポリシーを定義するときに要求を検査できます。One overload allows the request to be inspected when defining which policy to apply:

var timeout = Policy.TimeoutAsync<HttpResponseMessage>(
    TimeSpan.FromSeconds(10));
var longTimeout = Policy.TimeoutAsync<HttpResponseMessage>(
    TimeSpan.FromSeconds(30));

services.AddHttpClient("conditionalpolicy")
// Run some code to select a policy based on the request
    .AddPolicyHandler(request => 
        request.Method == HttpMethod.Get ? timeout : longTimeout);

上記のコードでは、送信要求が HTTP GET の場合は、10 秒のタイムアウトが適用されます。In the preceding code, if the outgoing request is an HTTP GET, a 10-second timeout is applied. 他の HTTP メソッドの場合は、30 秒のタイムアウトが使用されます。For any other HTTP method, a 30-second timeout is used.

複数の Polly ハンドラーを追加するAdd multiple Polly handlers

Polly ポリシーを入れ子にして拡張機能を提供するのが一般的です。It's common to nest Polly policies to provide enhanced functionality:

services.AddHttpClient("multiplepolicies")
    .AddTransientHttpErrorPolicy(p => p.RetryAsync(3))
    .AddTransientHttpErrorPolicy(
        p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));

前の例では、2 つのハンドラーが追加されています。In the preceding example, two handlers are added. 最初のハンドラーは、AddTransientHttpErrorPolicy 拡張メソッドを使用して再試行ポリシーを追加します。The first uses the AddTransientHttpErrorPolicy extension to add a retry policy. 失敗した要求は 3 回まで再試行されます。Failed requests are retried up to three times. AddTransientHttpErrorPolicy の 2 番目の呼び出しでは、サーキット ブレーカー ポリシーが追加されています。The second call to AddTransientHttpErrorPolicy adds a circuit breaker policy. 試行が連続して 5 回失敗した場合、それ以上の外部要求は 30 秒間ブロックされます。Further external requests are blocked for 30 seconds if five failed attempts occur sequentially. サーキット ブレーカー ポリシーはステートフルです。Circuit breaker policies are stateful. このクライアントからのすべての呼び出しは、同じサーキット状態を共有します。All calls through this client share the same circuit state.

Polly レジストリからポリシーを追加するAdd policies from the Polly registry

定期的に使用されるポリシーを管理するには、ポリシーを 1 回定義した後、PolicyRegistry でポリシーを登録します。An approach to managing regularly used policies is to define them once and register them with a PolicyRegistry. 提供されている拡張メソッドを使用することで、レジストリからポリシーを使用してハンドラーを追加できます。An extension method is provided which allows a handler to be added using a policy from the registry:

var registry = services.AddPolicyRegistry();

registry.Add("regular", timeout);
registry.Add("long", longTimeout);

services.AddHttpClient("regulartimeouthandler")
    .AddPolicyHandlerFromRegistry("regular");

上記のコードでは、PolicyRegistryServiceCollection に追加されるときに 2 つのポリシーが登録されています。In the preceding code, two policies are registered when the PolicyRegistry is added to the ServiceCollection. レジストリからポリシーを使用するには、適用するポリシーの名前を渡して AddPolicyHandlerFromRegistry メソッドを使用します。To use a policy from the registry, the AddPolicyHandlerFromRegistry method is used, passing the name of the policy to apply.

IHttpClientFactory および Polly の統合について詳しくは、Polly の Wiki をご覧ください。Further information about IHttpClientFactory and Polly integrations can be found on the Polly wiki.

HttpClient と有効期間の管理HttpClient and lifetime management

IHttpClientFactoryCreateClient を呼び出すたびに、HttpClient の新しいインスタンスが返されます。A new HttpClient instance is returned each time CreateClient is called on the IHttpClientFactory. 名前付きクライアントごとに HttpMessageHandler が存在します。There's an HttpMessageHandler per named client. ファクトリによって HttpMessageHandler インスタンスの有効期間が管理されます。The factory manages the lifetimes of the HttpMessageHandler instances.

IHttpClientFactory は、リソースの消費量を減らすため、ファクトリによって作成された HttpMessageHandler のインスタンスをプールします。IHttpClientFactory pools the HttpMessageHandler instances created by the factory to reduce resource consumption. 新しい HttpClient インスタンスを作成するときに、プールの HttpMessageHandler インスタンスの有効期間が切れていない場合はそれを再利用する場合があります。An HttpMessageHandler instance may be reused from the pool when creating a new HttpClient instance if its lifetime hasn't expired.

通常、各ハンドラーでは基になる HTTP 接続が独自に管理されるため、ハンドラーはプールすることが望まれます。Pooling of handlers is desirable as each handler typically manages its own underlying HTTP connections. 必要以上のハンドラーを作成すると、接続に遅延が発生する可能性があります。Creating more handlers than necessary can result in connection delays. また、一部のハンドラーは接続を無期限に開いており、DNS の変更にハンドラーが対応できないことがあります。Some handlers also keep connections open indefinitely, which can prevent the handler from reacting to DNS changes.

ハンドラーの既定の有効期間は 2 分です。The default handler lifetime is two minutes. 名前付きクライアントごとに、既定値をオーバーライドすることができます。The default value can be overridden on a per named client basis. オーバーライドするには、クライアント作成時に返された IHttpClientBuilderSetHandlerLifetime を呼び出します。To override it, call SetHandlerLifetime on the IHttpClientBuilder that is returned when creating the client:

services.AddHttpClient("extendedhandlerlifetime")
    .SetHandlerLifetime(TimeSpan.FromMinutes(5));

クライアントを破棄する必要はありません。Disposal of the client isn't required. 破棄すると送信要求がキャンセルされ、Dispose の呼び出し後には指定された HttpClient インスタンスを使用できなくなります。Disposal cancels outgoing requests and guarantees the given HttpClient instance can't be used after calling Dispose. IHttpClientFactory は、HttpClient インスタンスによって使用されたリソースの追跡と破棄を行います。IHttpClientFactory tracks and disposes resources used by HttpClient instances. 通常、HttpClient インスタンスは、破棄を必要としない .NET オブジェクトとして扱うことができます。The HttpClient instances can generally be treated as .NET objects not requiring disposal.

IHttpClientFactory の登場以前は、1 つの HttpClient インスタンスを長い期間使い続けるパターンが一般的に使用されていました。Keeping a single HttpClient instance alive for a long duration is a common pattern used before the inception of IHttpClientFactory. IHttpClientFactory に移行した後は、このパターンは不要になります。This pattern becomes unnecessary after migrating to IHttpClientFactory.

IHttpClientFactory の代替手段Alternatives to IHttpClientFactory

DI 対応のアプリ内で IHttpClientFactory を使用すれば、次のことを回避できます。Using IHttpClientFactory in a DI-enabled app avoids:

  • HttpMessageHandler インスタンスをプールすることによるリソース枯渇の問題。Resource exhaustion problems by pooling HttpMessageHandler instances.
  • 一定の間隔で HttpMessageHandler インスタンスを循環させることによって発生する古くなった DNS の問題。Stale DNS problems by cycling HttpMessageHandler instances at regular intervals.

有効期間の長い SocketsHttpHandler インスタンスを使用して、上記の問題を解決する別の方法があります。There are alternative ways to solve the preceding problems using a long-lived SocketsHttpHandler instance.

  • アプリの起動時に SocketsHttpHandler インスタンスを作成し、アプリの有効期間中、それを使用します。Create an instance of SocketsHttpHandler when the app starts and use it for the life of the app.
  • DNS の更新時間に基づいて、PooledConnectionLifetime を適切な値に構成します。Configure PooledConnectionLifetime to an appropriate value based on DNS refresh times.
  • 必要に応じて new HttpClient(handler, disposeHandler: false) を使用して HttpClient インスタンスを作成します。Create HttpClient instances using new HttpClient(handler, disposeHandler: false) as needed.

上記の方法を使用すると、IHttpClientFactory が同様の方法で解決するリソース管理の問題を解決できます。The preceding approaches solve the resource management problems that IHttpClientFactory solves in a similar way.

  • SocketsHttpHandler を使用すると、HttpClient インスタンス間で接続を共有できます。The SocketsHttpHandler shares connections across HttpClient instances. この共有によってソケットの枯渇が防止されます。This sharing prevents socket exhaustion.
  • SocketsHttpHandler では、古くなった DNS の問題を回避するために PooledConnectionLifetime に従って接続を循環されます。The SocketsHttpHandler cycles connections according to PooledConnectionLifetime to avoid stale DNS problems.

CookiesCookies

HttpMessageHandler インスタンスをプールすると、CookieContainer オブジェクトが共有されます。The pooled HttpMessageHandler instances results in CookieContainer objects being shared. 予期せぬ CookieContainer オブジェクト共有があると、多くの場合、コードは不適切なものとなります。Unanticipated CookieContainer object sharing often results in incorrect code. cookie を必要とするアプリの場合は、次のいずれかを検討してください。For apps that require cookies, consider either:

  • 自動的な cookie 処理の無効化Disabling automatic cookie handling
  • IHttpClientFactory の回避Avoiding IHttpClientFactory

ConfigurePrimaryHttpMessageHandler を呼び出して、自動的な cookie 処理を無効にします。Call ConfigurePrimaryHttpMessageHandler to disable automatic cookie handling:

services.AddHttpClient("configured-disable-automatic-cookies")
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        return new SocketsHttpHandler()
        {
            UseCookies = false,
        };
    });

ログの記録Logging

IHttpClientFactory によって作成されたクライアントは、すべての要求のログ メッセージを記録します。Clients created via IHttpClientFactory record log messages for all requests. 既定のログ メッセージを見るには、ログの構成で適切な情報レベルを有効にします。Enable the appropriate information level in your logging configuration to see the default log messages. 要求ヘッダーのログなどの追加ログは、トレース レベルでのみ含まれます。Additional logging, such as the logging of request headers, is only included at trace level.

各クライアントに使用されるログのカテゴリには、クライアントの名前が含まれます。The log category used for each client includes the name of the client. たとえば、 MyNamedClient という名前のクライアントは、カテゴリ System.Net.Http.HttpClient.MyNamedClient.LogicalHandler でメッセージをログに記録します。A client named MyNamedClient , for example, logs messages with a category of System.Net.Http.HttpClient.MyNamedClient.LogicalHandler. LogicalHandler というサフィックスが付いたメッセージが、要求ハンドラーのパイプラインの外部で発生します。Messages suffixed with LogicalHandler occur outside the request handler pipeline. 要求では、パイプライン内の他のハンドラーが要求を処理する前に、メッセージがログに記録されます。On the request, messages are logged before any other handlers in the pipeline have processed it. 応答では、他のパイプライン ハンドラーが応答を受け取った後で、メッセージがログに記録されます。On the response, messages are logged after any other pipeline handlers have received the response.

ログ記録は、要求ハンドラーのパイプラインの内部でも行われます。Logging also occurs inside the request handler pipeline. MyNamedClient の例では、それらのメッセージはログ カテゴリ System.Net.Http.HttpClient.MyNamedClient.ClientHandler に対して記録されます。In the MyNamedClient example, those messages are logged against the log category System.Net.Http.HttpClient.MyNamedClient.ClientHandler. 要求では、他のすべてのハンドラーが実行した後、要求がネットワークに送信される直前に、これが行われます。For the request, this occurs after all other handlers have run and immediately before the request is sent out on the network. 応答では、このログ記録には、ハンドラー パイプラインを通じ戻される前の応答の状態が含まれます。On the response, this logging includes the state of the response before it passes back through the handler pipeline.

パイプラインの外部と内部でログを有効にすると、他のパイプライン ハンドラーによる変更を検査できます。Enabling logging outside and inside the pipeline enables inspection of the changes made by the other pipeline handlers. これには、たとえば、要求ヘッダーや応答状態コードへの変更を含めることができます。This may include changes to request headers, for example, or to the response status code.

ログのカテゴリにクライアントの名前を含めると、必要に応じて、特定の名前付きクライアントでログをフィルター処理できます。Including the name of the client in the log category enables log filtering for specific named clients where necessary.

HttpMessageHandler を構成するConfigure the HttpMessageHandler

クライアントによって使用される内部 HttpMessageHandler の構成を制御することが必要な場合があります。It may be necessary to control the configuration of the inner HttpMessageHandler used by a client.

名前付きクライアントまたは型指定されたクライアントを追加すると、IHttpClientBuilder が返されます。An IHttpClientBuilder is returned when adding named or typed clients. ConfigurePrimaryHttpMessageHandler 拡張メソッドを使用して、デリゲートを定義することができます。The ConfigurePrimaryHttpMessageHandler extension method can be used to define a delegate. デリゲートは、そのクライアントによって使用されるプライマリ HttpMessageHandler の作成と構成に使用されます。The delegate is used to create and configure the primary HttpMessageHandler used by that client:

services.AddHttpClient("configured-inner-handler")
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        return new HttpClientHandler()
        {
            AllowAutoRedirect = false,
            UseDefaultCredentials = true
        };
    });

コンソール アプリで IHttpClientFactory を使用するUse IHttpClientFactory in a console app

コンソール アプリで、次のパッケージ参照をプロジェクトに追加します。In a console app, add the following package references to the project:

次に例を示します。In the following example:

  • IHttpClientFactory汎用ホストのサービス コンテナーに登録されます。IHttpClientFactory is registered in the Generic Host's service container.
  • MyService により、クライアント ファクトリ インスタンスがサービスから作成され、それが HttpClient の作成に使用されます。MyService creates a client factory instance from the service, which is used to create an HttpClient. HttpClient は Web ページを取得する目的で使用されます。HttpClient is used to retrieve a webpage.
  • Main により、サービスの GetPage メソッドを実行し、Web ページ コンテンツの最初の 500 文字をコンソールに書き込むためのスコープが作成されます。Main creates a scope to execute the service's GetPage method and write the first 500 characters of the webpage content to the console.
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
class Program
{
    static async Task<int> Main(string[] args)
    {
        var builder = new HostBuilder()
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHttpClient();
                services.AddTransient<IMyService, MyService>();
            }).UseConsoleLifetime();

        var host = builder.Build();

        using (var serviceScope = host.Services.CreateScope())
        {
            var services = serviceScope.ServiceProvider;

            try
            {
                var myService = services.GetRequiredService<IMyService>();
                var pageContent = await myService.GetPage();

                Console.WriteLine(pageContent.Substring(0, 500));
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();

                logger.LogError(ex, "An error occurred.");
            }
        }

        return 0;
    }

    public interface IMyService
    {
        Task<string> GetPage();
    }

    public class MyService : IMyService
    {
        private readonly IHttpClientFactory _clientFactory;

        public MyService(IHttpClientFactory clientFactory)
        {
            _clientFactory = clientFactory;
        }

        public async Task<string> GetPage()
        {
            // Content from BBC One: Dr. Who website (©BBC)
            var request = new HttpRequestMessage(HttpMethod.Get,
                "https://www.bbc.co.uk/programmes/b006q2x0");
            var client = _clientFactory.CreateClient();
            var response = await client.SendAsync(request);

            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadAsStringAsync();
            }
            else
            {
                return $"StatusCode: {response.StatusCode}";
            }
        }
    }
}

ヘッダー伝達ミドルウェアHeader propagation middleware

ヘッダー伝達は、受信要求から送信 HTTP クライアント要求に HTTP ヘッダーを伝達するためのコミュニティでサポートされたミドルウェアです。Header propagation is a community supported middleware to propagate HTTP headers from the incoming request to the outgoing HTTP Client requests. ヘッダー伝達を使用するには、次を行います。To use header propagation:

  • パッケージ HeaderPropagation のコミュニティでサポートされているポートを参照します。Reference the community supported port of the package HeaderPropagation. ASP.NET Core 3.1 以降では、Microsoft.AspNetCore.HeaderPropagation がサポートされています。ASP.NET Core 3.1 and later supports Microsoft.AspNetCore.HeaderPropagation.

  • Startup でミドルウェアと HttpClient を構成します。Configure the middleware and HttpClient in Startup:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    
        services.AddHttpClient("MyForwardingClient").AddHeaderPropagation();
        services.AddHeaderPropagation(options =>
        {
            options.Headers.Add("X-TraceId");
        });
    }
    
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }
    
        app.UseHttpsRedirection();
    
        app.UseHeaderPropagation();
    
        app.UseMvc();
    }
    
  • クライアントで、構成されたヘッダーが送信要求に含まれます。The client includes the configured headers on outbound requests:

    var client = clientFactory.CreateClient("MyForwardingClient");
    var response = client.GetAsync(...);
    

その他の技術情報Additional resources