IHttpClientFactory s .NET

V tomto článku se dozvíte, jak používat IHttpClientFactory k vytváření HttpClient typů s různými základy .NET, jako je injektáž závislostí (DI), protokolování a konfigurace. Typ HttpClient byl zaveden v rozhraní .NET Framework 4.5, který byl vydán v roce 2012. Jinými slovy, je to už nějakou dobu. HttpClient slouží k vytváření požadavků HTTP a zpracování odpovědí HTTP z webových prostředků identifikovaných pomocí Uri. Protokol HTTP tvoří velkou většinu veškerého internetového provozu.

Díky moderním principům vývoje aplikací, které řídí osvědčené postupy, IHttpClientFactory slouží jako abstrakce továrny, která může vytvářet HttpClient instance s vlastními konfiguracemi. IHttpClientFactory byla zavedena v .NET Core 2.1. Běžné úlohy .NET založené na protokolu HTTP můžou snadno využívat odolný a přechodný middleware pro zpracování chyb třetích stran.

Poznámka:

Pokud vaše aplikace vyžaduje soubory cookie, může být lepší se vyhnout použití IHttpClientFactory ve vaší aplikaci. Alternativní způsoby správy klientů najdete v tématu Pokyny pro používání klientů HTTP.

Důležité

Správa životnosti instancí vytvořených HttpClientIHttpClientFactory ručně se liší od instancí vytvořených ručně. Strategie jsou použití krátkodobých klientů vytvořených klienty IHttpClientFactory nebo dlouhodobých klientů s PooledConnectionLifetime nastavením. Další informace najdete v části Správa životnosti HttpClient a pokyny pro používání klientů HTTP.

Typ IHttpClientFactory

Veškerý ukázkový zdrojový kód v tomto článku závisí na Microsoft.Extensions.Http balíčku NuGet. Kromě toho se požadavky HTTP GET provádějí do bezplatného zástupného rozhraní API {JSON}, aby získalo objekty uživatele Todo .

Když voláte některou AddHttpClient z metod rozšíření, přidáváte do IHttpClientFactoryIServiceCollectionslužby a související služby . Typ IHttpClientFactory nabízí následující výhody:

  • HttpClient Zveřejňuje třídu jako typ připravený k di-ready.
  • Poskytuje centrální umístění pro pojmenování a konfiguraci logických instancí HttpClient.
  • Kodifikuje koncept odchozího middlewaru prostřednictvím delegování obslužných rutin v HttpClient.
  • Poskytuje rozšiřující metody pro Polly založený middleware, které využívají delegování obslužných rutin v HttpClient.
  • Spravuje ukládání do mezipaměti a životnost základních HttpClientHandler instancí. Automatická správa zabraňuje běžným problémům dns (Domain Name System), ke kterým dochází při ruční správě HttpClient životnosti.
  • Přidá konfigurovatelné protokolování (prostřednictvím ILogger) pro všechny požadavky odeslané prostřednictvím klientů vytvořených továrnou.

Spotřeby

V aplikaci můžete použít několik způsobů IHttpClientFactory :

Nejlepší přístup závisí na požadavcích aplikace.

Základní použití

Chcete-li zaregistrovat IHttpClientFactory, zavolejte AddHttpClient:

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

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

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

using IHost host = builder.Build();

Využívání služeb může vyžadovat parametr konstruktoru IHttpClientFactory s DI. Následující kód používá IHttpClientFactory k vytvoření HttpClient instance:

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

namespace BasicHttp.Example;

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

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

        return [];
    }
}

Použití IHttpClientFactory podobné jako v předchozím příkladu je dobrým způsobem, jak refaktorovat existující aplikaci. Nemá žádný vliv na způsob HttpClient použití. V místech, kde HttpClient se instance vytvářejí v existující aplikaci, nahraďte tyto výskyty voláními CreateClient.

Pojmenovaní klienti

Pojmenovaní klienti jsou dobrou volbou, když:

  • Aplikace vyžaduje mnoho různých použití HttpClient.
  • Mnoho HttpClient instancí má různé konfigurace.

Konfiguraci pojmenovaného HttpClient názvu lze zadat během registrace v IServiceCollection:

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

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

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

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

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

V předchozím kódu je klient nakonfigurovaný takto:

  • Název, který se načítá z konfigurace pod položkou "TodoHttpClientName".
  • Základní adresa https://jsonplaceholder.typicode.com/.
  • Záhlaví "User-Agent" .

Konfiguraci můžete použít k určení názvů klientů HTTP, což je užitečné, abyste se vyhnuli nesprávnému přejmenování klientů při přidávání a vytváření. V tomto příkladu se k konfiguraci názvu klienta HTTP používá soubor appsettings.json :

{
    "TodoHttpClientName": "JsonPlaceholderApi"
}

Tuto konfiguraci můžete snadno rozšířit a uložit další podrobnosti o tom, jak má klient HTTP fungovat. Další informace naleznete v tématu Konfigurace v .NET.

Vytvoření klienta

Pokaždé CreateClient se volá:

  • Vytvoří se nová instance HttpClient .
  • Volá se akce konfigurace.

Pokud chcete vytvořit pojmenovaného klienta, předejte jeho název do CreateClient:

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

namespace NamedHttp.Example;

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

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

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

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

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

        return [];
    }
}

V předchozím kódu požadavek HTTP nemusí zadávat název hostitele. Kód může předat pouze cestu, protože se používá základní adresa nakonfigurovaná pro klienta.

Typoví klienti

Typoví klienti:

  • Poskytněte stejné možnosti jako pojmenovaní klienti bez nutnosti používat řetězce jako klíče.
  • Poskytnutí nápovědy IntelliSense a kompilátoru při využívání klientů
  • Zadejte jedno umístění pro konfiguraci a interakci s určitým HttpClient. Může se například použít jeden typ klienta:
    • Pro jeden back-endový koncový bod.
    • Zapouzdření veškeré logiky, která se zabývá koncovým bodem.
  • Pracujte s DI a můžete je v aplikaci vsunou tam, kde je to potřeba.

Zadaný klient přijímá parametr v jeho konstruktoru HttpClient :

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

namespace TypedHttp.Example;

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

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

        return [];
    }

    public void Dispose() => httpClient?.Dispose();
}

V předchozím kódu:

  • Konfigurace se nastaví při přidání zadaného klienta do kolekce služeb.
  • Je HttpClient přiřazena jako proměnná s oborem třídy (pole) a používá se s vystavenými rozhraními API.

Metody specifické pro rozhraní API je možné vytvořit, které zpřístupňují HttpClient funkce. GetUserTodosAsync Například metoda zapouzdřuje kód pro načtení objektů specifických pro Todo uživatele.

Následující volání AddHttpClient kódu pro registraci typové třídy klienta:

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

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

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

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

Zadaný klient je zaregistrovaný jako přechodný s DI. V předchozím kódu AddHttpClient se zaregistruje TodoService jako přechodná služba. Tato registrace používá metodu továrny k:

  1. Vytvořte instanci HttpClient.
  2. Vytvoření instance , TodoServicepředání instance HttpClient do jeho konstruktoru.

Důležité

Používání typových klientů v jednoúčelových službách může být nebezpečné. Další informace najdete v části Vyhnout se typed klientům v části Singleton Services .

Poznámka:

Při registraci zadaného klienta v AddHttpClient<TClient> metodě musí TClient mít typ konstruktor, který přijímá HttpClient parametr. Kromě toho TClient by se typ neměl registrovat v kontejneru DI samostatně.

Pojmenovaní a typoví klienti

Pojmenovaní klienti a typoví klienti mají své vlastní silné a slabé stránky. Existuje způsob, jak tyto dva typy klientů kombinovat, abyste získali to nejlepší z obou světů.

Primární případ použití je následující: Použijte stejného typu klienta, ale pro různé domény. Máte například primární a sekundární službu a zpřístupňují přesně stejné funkce. To znamená, že ke zabalení HttpClient využití k vydávání požadavků, zpracování odpovědí a zpracování chyb můžete použít stejného typu klienta. Stejný kód se použije, ale s různými konfiguracemi (například s jinou základní adresou, vypršením časového limitu a přihlašovacími údaji).

Následující příklad používá stejného TodoService typu klienta, který byl zobrazen v části typ klienti .

Nejprve zaregistrujte pojmenované a zadané klienty.

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

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddHttpClient<TodoService>("primary"
    client =>
    {
        // Configure the primary typed client
        client.BaseAddress = new Uri("https://primary-host-address.com/");
        client.Timeout = TimeSpan.FromSeconds(3);
    });

// Register the same typed client but with different settings
builder.Services.AddHttpClient<TodoService>("secondary"
    client =>
    {
        // Configure the secondary typed client
        client.BaseAddress = new Uri("https://secondary-host-address.com/");
        client.Timeout = TimeSpan.FromSeconds(10);
    });

V předchozím kódu:

  • První AddHttpClient volání zaregistruje zadaného TodoService klienta pod primary názvem. Základní HttpClient body na primární službu a mají krátký časový limit.
  • Druhé AddHttpClient volání zaregistruje zadaného TodoService klienta pod secondary názvem. Základní HttpClient body sekundární služby a mají delší časový limit.
using IHost host = builder.Build();

// Fetch an IHttpClientFactory instance to create a named client
IHttpClientFactory namedClientFactory =
    host.Services.GetRequiredService<IHttpClientFactory>();

// Fetch an ITypedHttpClientFactory<TodoService> instance to create a named and typed client
ITypedHttpClientFactory<TodoService> typedClientFactory  =
    host.Services.GetRequiredService<ITypedHttpClientFactory<TodoService>>();

// Create a TodoService instance against the primary host
var primaryClient = namedClientFactory.CreateClient("primary");
var todoService = typedClientFactory.CreateClient(primaryClient);

V předchozím kódu:

  • Instance IHttpClientFactory se načte z kontejneru DI, aby bylo možné vytvořit pojmenované klienty prostřednictvím své CreateClient metody.
  • Instance ITypedHttpClientFactory<TodoService> se načte z kontejneru DI, aby bylo možné vytvářet typované klienty prostřednictvím své CreateClient metody.
    • Toto CreateClient přetížení přijalo název HttpClient (se správnou konfigurací) jako jeho parametr.
    • todoService Vytvoření je nakonfigurováno tak, aby používalo primární službu.

Poznámka:

Typ IHttpClientFactory se nachází uvnitř System.Net.Http oborů názvů, zatímco ITypedHttpClientFactory typ uvnitř objektu Microsoft.Extensions.Http.

Důležité

Použijte implementační třídu (v předchozím příkladu TodoService) jako parametr typu pro ITypedHttpClientFactory. I když máte abstrakci (například ITodoService rozhraní), musíte i tuto implementaci použít. Pokud omylem použijete abstrakci (ITodoService), pak při volání CreateClient jeho vyvolá .InvalidOperationException

try
{
    Todo[] todos = await todoService.GetUserTodosAsync(4);
}
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
{
    // The request timed out against the primary host

    // Create a TodoService instance against the secondary host
    var fallbackClient = namedClientFactory.CreateClient("secondary");
    var todoFallbackService = typedClientFactory.CreateClient(fallbackClient);

    // Issue request against the secondary host
    Todo[] todos = await todoFallbackService.GetUserTodosAsync(4);
}

V předchozím kódu:

  • Pokouší se vydat požadavek na primární službu.
  • Pokud vyprší časový limit požadavku (trvá déle než 3 sekundy TaskCanceledExceptionTimeoutException ), vyvolá se vnitřní hodnota.
  • V případě vypršení časového limitu se vytvoří nový klient a použije se, který teď cílí na sekundární službu.

Vygenerované klienty

IHttpClientFactory lze použít v kombinaci s knihovnami třetích stran, jako je například Refit. Refit je knihovna REST pro .NET. Umožňuje deklarativní definice rozhraní REST API, metody mapování rozhraní na koncové body. Implementace rozhraní se generuje dynamicky RestServicepomocí , pomocí HttpClient k provedení externích volání HTTP.

Zvažte následující record typ:

namespace Shared;

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

Následující příklad spoléhá na Refit.HttpClientFactory balíček NuGet a je to jednoduché rozhraní:

using Refit;
using Shared;

namespace GeneratedHttp.Example;

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

Předchozí rozhraní jazyka C#:

  • Definuje metodu s názvem GetUserTodosAsync , která vrací Task<Todo[]> instanci.
  • Deklaruje Refit.GetAttribute atribut s cestou a řetězcem dotazu pro externí rozhraní API.

Typový klient je možné přidat pomocí nástroje Refit k vygenerování implementace:

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

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

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

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

Definované rozhraní lze v případě potřeby využívat s implementací, kterou poskytuje DI a Refit.

Vytváření požadavků POST, PUT a DELETE

V předchozích příkladech používají GET všechny požadavky HTTP příkaz HTTP. HttpClient také podporuje další příkazy HTTP, včetně:

  • POST
  • PUT
  • DELETE
  • PATCH

Úplný seznam podporovaných příkazů HTTP najdete v tématu HttpMethod. Další informace o vytváření požadavků HTTP naleznete v tématu Odeslání požadavku pomocí HttpClient.

Následující příklad ukazuje, jak vytvořit požadavek HTTP POST :

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

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

    httpResponse.EnsureSuccessStatusCode();
}

V předchozím kódu CreateItemAsync metoda:

  • Serializuje Item parametr do FORMÁTU JSON pomocí System.Text.Json. To používá instanci JsonSerializerOptions ke konfiguraci procesu serializace.
  • Vytvoří instanci pro zabalení serializovaného StringContent JSON pro odeslání v těle požadavku HTTP.
  • Volání PostAsync pro odeslání obsahu JSON na zadanou adresu URL Toto je relativní adresa URL, která se přidá do HttpClient.BaseAddress.
  • Volání EnsureSuccessStatusCode vyvolání výjimky, pokud stavový kód odpovědi nezjistí úspěch.

HttpClient podporuje také jiné typy obsahu. Příklad: MultipartContent a StreamContent. Úplný seznam podporovaného obsahu najdete v tématu HttpContent.

Následující příklad ukazuje požadavek HTTP PUT :

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

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

    httpResponse.EnsureSuccessStatusCode();
}

Předchozí kód je velmi podobný příkladu POST . Metoda UpdateItemAsync volá PutAsync místo PostAsync.

Následující příklad ukazuje požadavek HTTP DELETE :

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

    httpResponse.EnsureSuccessStatusCode();
}

V předchozím kódu DeleteItemAsync metoda volá DeleteAsync. Vzhledem k tomu, že požadavky HTTP DELETE obvykle neobsahují žádný text, DeleteAsync metoda neposkytuje přetížení, které přijímá instanci HttpContent.

Další informace o použití různých příkazů HTTP s HttpClient, viz HttpClient.

HttpClient správa životnosti

Nová HttpClient instance se vrátí pokaždé, když CreateClient je volána na IHttpClientFactory. Pro každý název klienta se vytvoří jedna HttpClientHandler instance. Továrna spravuje životnosti HttpClientHandler instancí.

IHttpClientFactoryHttpClientHandler ukládá instance vytvořené továrnou do mezipaměti, aby se snížila spotřeba prostředků. Instance HttpClientHandler může být znovu použita z mezipaměti při vytváření nové HttpClient instance, pokud jeho životnost nevypršela.

Ukládání do mezipaměti obslužných rutin je žádoucí, protože každá obslužná rutina obvykle spravuje svůj vlastní základní fond připojení HTTP. Vytvoření více obslužných rutin, než je potřeba, může vést k vyčerpání soketů a zpoždění připojení. Některé obslužné rutiny také trvale udržují připojení otevřená, což může obslužné rutině zabránit v reakci na změny DNS.

Výchozí životnost obslužné rutiny je dvě minuty. Pokud chcete přepsat výchozí hodnotu, zavolejte SetHandlerLifetime pro každého klienta na :IServiceCollection

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

Důležité

HttpClient Instance vytvořené pomocí IHttpClientFactory jsou určeny k krátkodobému použití.

  • Recyklace a opětovné vytvoření HttpMessageHandlerpo vypršení jejich životnosti je nezbytné, IHttpClientFactory aby obslužné rutiny reagovaly na změny DNS. HttpClient je svázaný s konkrétní instancí obslužné rutiny při jeho vytvoření, takže nové HttpClient instance by měly být požadovány včas, aby se zajistilo, že klient získá aktualizovanou obslužnou rutinu.

  • Vyřazení takových HttpClient instancí vytvořených továrnou nebude vést k vyčerpání soketů, protože jeho odstranění nespustí odstranění HttpMessageHandler. IHttpClientFactory sleduje a odstraňuje prostředky používané k vytváření HttpClient instancí, konkrétně HttpMessageHandler instancí, jakmile jejich životnost vyprší a už je nepoužívá HttpClient .

Udržování jedné HttpClient instance naživu po dlouhou dobu je běžný vzor, který lze použít jako alternativu IHttpClientFactoryk , ale tento vzor vyžaduje další nastavení, například PooledConnectionLifetime. Můžete použít dlouhodobé klienty s PooledConnectionLifetimenebo krátkodobými klienty vytvořenými .IHttpClientFactory Informace o tom, jakou strategii použít ve vaší aplikaci, najdete v tématu Pokyny pro používání klientů HTTP.

Konfigurace HttpMessageHandler

Může být nutné řídit konfiguraci vnitřního HttpMessageHandler prostředí používaného klientem.

Vrátí IHttpClientBuilder se při přidávání pojmenovaných nebo zadaných klientů. Metodu ConfigurePrimaryHttpMessageHandler rozšíření lze použít k definování delegáta na .IServiceCollection Delegát se používá k vytvoření a konfiguraci primárního HttpMessageHandler klienta, který tento klient používá:

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

Konfigurace HttClientHandler umožňuje zadat proxy pro HttpClient instanci mezi různými dalšími vlastnostmi obslužné rutiny. Další informace najdete v tématu Proxy na klienta.

Další konfigurace

Existuje několik dalších možností konfigurace pro řízení IHttpClientHandler:

metoda Popis
AddHttpMessageHandler Přidá další obslužnou rutinu zprávy pro pojmenovanou HttpClient.
AddTypedClient Konfiguruje vazbu mezi TClient a pojmenovanou HttpClient přidruženou k sadě IHttpClientBuilder.
ConfigureHttpClient Přidá delegáta, který se použije ke konfiguraci pojmenovaného HttpClient.
ConfigureHttpMessageHandlerBuilder Přidá delegáta, který bude použit ke konfiguraci obslužných rutin zpráv pomocí HttpMessageHandlerBuilder pro pojmenovaný HttpClient.
ConfigurePrimaryHttpMessageHandler Nakonfiguruje primární HttpMessageHandler kontejner injektáže závislostí pro pojmenovanou HttpClient.
RedactLoggedHeaders Nastaví kolekci názvů hlaviček HTTP, pro které mají být hodnoty před protokolováním upraveny.
SetHandlerLifetime Nastaví dobu, po kterou HttpMessageHandler může být instance znovu použita. Každý pojmenovaný klient může mít svou vlastní nakonfigurovanou hodnotu životnosti obslužné rutiny.

Použití IHttpClientFactory společně se SoketsHttpHandler

Implementace SocketsHttpHandlerHttpMessageHandler byla přidána v .NET Core 2.1, což umožňuje PooledConnectionLifetime konfiguraci. Toto nastavení slouží k zajištění toho, aby obslužná rutina reagovala na změny DNS, takže použití SocketsHttpHandler se považuje za alternativu k použití IHttpClientFactory. Další informace najdete v tématu Pokyny pro používání klientů HTTP.

SocketsHttpHandler Dá se ale použít společně a IHttpClientFactory zlepšit konfigurovatelnost. Pomocí obou těchto rozhraní API můžete využít možnosti konfigurace na nízké úrovni (například pro LocalCertificateSelectionCallback výběr dynamického certifikátu) a vysokou úroveň (například využití integrace DI a několika konfigurací klientů).

Použití obou rozhraní API:

  1. Zadejte SocketsHttpHandler jako PrimaryHandler a nastavte jeho PooledConnectionLifetime (například na hodnotu, která byla dříve zadána HandlerLifetime).
  2. Jak SocketsHttpHandler bude zpracovávat sdružování a recyklaci připojení, nebude už potřeba recyklace obslužné rutiny na IHttpClientFactory úrovni. Můžete ho zakázat nastavením HandlerLifetime na Timeout.InfiniteTimeSpan.
services.AddHttpClient(name)
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        return new SocketsHttpHandler()
        {
            PooledConnectionLifetime = TimeSpan.FromMinutes(2)
        };
    })
    .SetHandlerLifetime(Timeout.InfiniteTimeSpan); // Disable rotation, as it is handled by PooledConnectionLifetime

Vyhněte se typem klientů v jednoúčelových službách

Při použití pojmenovaného klientského přístupu se IHttpClientFactory vloží do služeb a HttpClient instance se vytvoří voláním CreateClient pokaždé, když HttpClient je potřeba.

Při přístupu typu klienta jsou však typoví klienti přechodnými objekty obvykle vloženými do služeb. To může způsobit problém, protože typový klient lze vložit do jednoúčelové služby.

Důležité

Očekává se, že typoví klienti budou krátkodobě shodní s HttpClient instancemi vytvořenými IHttpClientFactory (další informace najdete v tématu HttpClient správa životnosti). Jakmile se vytvoří zatypovaná instance klienta, IHttpClientFactory nemá nad ní žádnou kontrolu. Pokud je zachytává zaznamenaná instance typu klienta v jednomtonu, může zabránit tomu, aby reagovala na změny DNS, a porazit jeden z účelů IHttpClientFactory.

Pokud potřebujete používat HttpClient instance ve službě singleton, zvažte následující možnosti:

  • Místo toho použijte pojmenovaný klientský přístup, vkážou IHttpClientFactory se do služby Singleton a v případě potřeby znovu vytvoří HttpClient instance.
  • Pokud potřebujete přístup typu klienta , použijte SocketsHttpHandler s nakonfigurovanou PooledConnectionLifetime primární obslužnou rutinou. Další informace o použití s SocketsHttpHandlerIHttpClientFactory, naleznete v části Použití IHttpClientFactory společně s SocketsHttpHandler.

Obory obslužné rutiny zpráv v IHttpClientFactory

IHttpClientFactory vytvoří pro každou HttpMessageHandler instanci samostatný obor DI. Tyto obory DI jsou oddělené od oborů DI aplikací (například ASP.NET rozsah příchozích požadavků nebo uživatelem vytvořený ruční obor DI), takže nebudou sdílet instance služby s vymezeným oborem. Obory obslužné rutiny zpráv jsou svázané s životností obslužné rutiny a můžou vést k obnovení rozsahů aplikace, což může vést například k opakovanému použití stejné instance se stejnými HttpMessageHandler vloženými vymezenými závislostmi mezi několika příchozími požadavky.

Diagram znázorňující dva obory DI aplikace a samostatný obor obslužné rutiny zpráv

Uživatelé důrazně nedoporučuje ukládat informace související s oborem (například data z HttpContext) do mezipaměti v HttpMessageHandler instancích a používat vymezené závislosti s opatrností, aby nedošlo k úniku citlivých informací.

Pokud potřebujete přístup k oboru DI aplikace z obslužné rutiny zprávy, jako příklad byste zapouzdřili logiku podporující obor v samostatné přechodném DelegatingHandlerobjektu IHttpClientFactory a zabalili ji kolem HttpMessageHandler instance z mezipaměti. Přístup k volání IHttpMessageHandlerFactory.CreateHandler obslužné rutiny pro všechny registrované pojmenované klienty. V takovém případě byste sami vytvořili HttpClient instanci pomocí vytvořené obslužné rutiny.

Diagram znázorňující získání přístupu k oborům DI aplikace prostřednictvím samostatné obslužné rutiny přechodných zpráv a IHttpMessageHandlerFactory

Následující příklad ukazuje vytvoření s HttpClient podporou oboru DelegatingHandler:

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

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

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

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

HttpClient client = new(handler);

Další alternativní řešení může následovat s metodou rozšíření pro registraci výchozí registrace s podporou DelegatingHandler oboru a přepsání výchozí IHttpClientFactory registrace přechodnou službou s přístupem k aktuálnímu oboru aplikace:

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

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

    return builder;
}

Další informace najdete v úplném příkladu.

Viz také