Udostępnij za pośrednictwem


IHttpClientFactory z platformą .NET

W tym artykule dowiesz się, jak używać interfejsu IHttpClientFactory do tworzenia HttpClient typów z różnymi podstawami platformy .NET, takimi jak wstrzykiwanie zależności (DI), rejestrowanie i konfiguracja. Typ HttpClient został wprowadzony w programie .NET Framework 4.5, który został wydany w 2012 roku. Innymi słowy, to było od jakiegoś czasu. HttpClient służy do tworzenia żądań HTTP i obsługi odpowiedzi HTTP z zasobów internetowych zidentyfikowanych przez Urielement . Protokół HTTP stanowi zdecydowaną większość całego ruchu internetowego.

Dzięki nowoczesnym zasadom tworzenia aplikacji, które napędzają najlepsze rozwiązania, usługa służy jako abstrakcja fabryki, IHttpClientFactory która może tworzyć HttpClient wystąpienia z konfiguracjami niestandardowymi. IHttpClientFactory wprowadzono w programie .NET Core 2.1. Typowe obciążenia platformy .NET oparte na protokole HTTP mogą z łatwością korzystać z odpornego i przejściowego oprogramowania pośredniczącego innych firm.

Uwaga

Jeśli aplikacja wymaga plików cookie, lepszym rozwiązaniem może być unikanie korzystania z IHttpClientFactory aplikacji. Aby uzyskać alternatywne sposoby zarządzania klientami, zobacz Wytyczne dotyczące korzystania z klientów HTTP.

Ważne

Zarządzanie okresem istnienia wystąpień utworzonych HttpClient przez IHttpClientFactory program jest zupełnie inne niż wystąpienia utworzone ręcznie. Strategie to użycie krótkoterminowych klientów utworzonych przez IHttpClientFactory klientów lub klientów długotrwałych z konfiguracjąPooledConnectionLifetime. Aby uzyskać więcej informacji, zobacz sekcję Zarządzanie okresem istnienia klienta HttpClient i wskazówki dotyczące korzystania z klientów HTTP.

Typ IHttpClientFactory

Cały przykładowy kod źródłowy podany w tym artykule wymaga zainstalowania Microsoft.Extensions.Http pakietu NuGet. Ponadto przykłady kodu pokazują użycie żądań HTTP GET w celu pobrania obiektów użytkownika Todo z bezpłatnego interfejsu API symbolu zastępczego {JSON}.

Podczas wywoływania dowolnej metody AddHttpClient rozszerzenia dodajesz IHttpClientFactory usługi i powiązane z nimi usługi do elementu IServiceCollection. Typ IHttpClientFactory oferuje następujące korzyści:

  • Uwidacznia klasę HttpClient jako typ gotowy do di.
  • Zapewnia centralną lokalizację nazewnictwa i konfigurowania wystąpień logicznych HttpClient.
  • Koduje koncepcję wychodzącego oprogramowania pośredniczącego za pośrednictwem delegowania programów obsługi w programie HttpClient.
  • Udostępnia metody rozszerzeń oprogramowania pośredniczącego opartego na usłudze Polly, aby korzystać z delegowania programów obsługi w programie HttpClient.
  • Zarządza buforowaniem i okresem istnienia wystąpień bazowych HttpClientHandler . Automatyczne zarządzanie pozwala uniknąć typowych problemów z systemem nazw domen (DNS), które występują podczas ręcznego zarządzania HttpClient okresami istnienia.
  • Dodaje konfigurowalne środowisko rejestrowania (za pośrednictwem ILogger) dla wszystkich żądań wysyłanych za pośrednictwem klientów utworzonych przez fabrykę.

Konsumpcji

W aplikacji można użyć kilku sposobów IHttpClientFactory :

Najlepsze podejście zależy od wymagań aplikacji.

Podstawowy sposób użycia

Aby zarejestrować obiekt , wywołaj metodę IHttpClientFactoryAddHttpClient:

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();

Korzystanie z usług może wymagać parametru IHttpClientFactory jako konstruktora z di. Poniższy kod używa IHttpClientFactory metody do utworzenia HttpClient wystąpienia:

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 [];
    }
}

Użycie IHttpClientFactory metody podobnej w poprzednim przykładzie jest dobrym sposobem refaktoryzacji istniejącej aplikacji. Nie ma to wpływu na sposób HttpClient użycia. W miejscach, w których HttpClient wystąpienia są tworzone w istniejącej aplikacji, zastąp te wystąpienia wywołaniami metody CreateClient.

Nazwani klienci

Nazwani klienci są dobrym wyborem w następujących przypadkach:

  • Aplikacja wymaga wielu różnych zastosowań programu HttpClient.
  • Wiele HttpClient wystąpień ma różne konfiguracje.

Konfigurację nazwy HttpClient można określić podczas rejestracji w pliku 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");
    });

W poprzednim kodzie klient jest skonfigurowany przy użyciu następujących elementów:

  • Nazwa pobrana z konfiguracji w obszarze "TodoHttpClientName".
  • Adres https://jsonplaceholder.typicode.com/podstawowy .
  • "User-Agent" Nagłówek.

Konfigurację można użyć do określenia nazw klientów HTTP, co jest pomocne w celu uniknięcia błędu klientów podczas dodawania i tworzenia. W tym przykładzie plik appsettings.json służy do konfigurowania nazwy klienta HTTP:

{
    "TodoHttpClientName": "JsonPlaceholderApi"
}

Można łatwo rozszerzyć tę konfigurację i zapisać więcej szczegółów na temat sposobu działania klienta HTTP. Aby uzyskać więcej informacji, zobacz Konfiguracja na platformie .NET.

Tworzenie klienta

Za każdym razem CreateClient jest wywoływana:

  • Zostanie utworzone nowe wystąpienie programu HttpClient .
  • Akcja konfiguracji jest wywoływana.

Aby utworzyć nazwanego klienta, przekaż jego nazwę 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 [];
    }
}

W poprzednim kodzie żądanie HTTP nie musi określać nazwy hosta. Kod może przekazać tylko ścieżkę, ponieważ używany jest adres podstawowy skonfigurowany dla klienta.

Wpisanych klientów

Typizowane klienci:

  • Zapewnij te same możliwości co nazwani klienci bez konieczności używania ciągów jako kluczy.
  • Zapewnianie funkcji IntelliSense i kompilatora pomocy podczas korzystania z klientów.
  • Podaj pojedynczą lokalizację do skonfigurowania i interakcji z określonym HttpClientelementem . Na przykład można użyć jednego typu klienta:
    • W przypadku pojedynczego punktu końcowego zaplecza.
    • Aby hermetyzować całą logikę do obsługi punktu końcowego.
  • Praca z di i może być wstrzykiwana tam, gdzie jest to wymagane w aplikacji.

Typowany klient akceptuje HttpClient parametr w konstruktorze:

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();
}

Powyższy kod:

  • Konfiguracja jest ustawiana po dodaniu wpisanego klienta do kolekcji usług.
  • Element HttpClient jest przypisywany jako zmienna o zakresie klasy (pole) i używany z uwidocznionych interfejsów API.

Metody specyficzne dla interfejsu API można utworzyć, aby uwidaczniać HttpClient funkcje. Na przykład GetUserTodosAsync metoda hermetyzuje kod w celu pobrania obiektów specyficznych dla Todo użytkownika.

Następujące wywołania AddHttpClient kodu w celu zarejestrowania wpisanej klasy 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");
    });

Typowany klient jest zarejestrowany jako przejściowy z di. W poprzednim kodzie AddHttpClient rejestruje się TodoService jako usługa przejściowa. Ta rejestracja używa metody fabryki do:

  1. Utwórz wystąpienie elementu HttpClient.
  2. Utwórz wystąpienie klasy , przekazując wystąpienie TodoServicedo jego konstruktora HttpClient .

Ważne

Korzystanie z typowanych klientów w usługach jednotonowych może być niebezpieczne. Aby uzyskać więcej informacji, zobacz sekcję Unikanie typiowanych klientów w usługach jednotonowych .

Uwaga

Podczas rejestrowania typizowanego klienta za AddHttpClient<TClient> pomocą metody TClient typ musi mieć konstruktor, który akceptuje HttpClient jako parametr. TClient Ponadto typ nie powinien być zarejestrowany w kontenerze di oddzielnie, ponieważ spowoduje to późniejsze zastąpienie poprzedniej rejestracji.

Wygenerowani klienci

IHttpClientFactory można używać w połączeniu z bibliotekami innych firm, takimi jak Refit. Refit to biblioteka REST dla platformy .NET. Umożliwia ona deklaratywne definicje interfejsu API REST, mapowanie metod interfejsu na punkty końcowe. Implementacja interfejsu jest generowana dynamicznie przez RestServiceelement , za pomocą polecenia HttpClient w celu wykonywania zewnętrznych wywołań HTTP.

Rozważ następujący record typ:

namespace Shared;

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

Poniższy przykład opiera się na pakiecie Refit.HttpClientFactory NuGet i jest prostym interfejsem:

using Refit;
using Shared;

namespace GeneratedHttp.Example;

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

Poprzedni interfejs języka C#:

  • Definiuje metodę o nazwie GetUserTodosAsync zwracającą Task<Todo[]> wystąpienie.
  • Refit.GetAttribute Deklaruje atrybut ze ścieżką i ciągiem zapytania do zewnętrznego interfejsu API.

Można dodać typizowanego klienta, używając polecenia Refit w celu wygenerowania implementacji:

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");
    });

Zdefiniowany interfejs można używać w razie potrzeby z implementacją dostarczaną przez di i refit.

Żądania POST, PUT i DELETE

W poprzednich przykładach wszystkie żądania HTTP używają czasownika GET HTTP. HttpClient Obsługuje również inne czasowniki HTTP, w tym:

  • POST
  • PUT
  • DELETE
  • PATCH

Aby uzyskać pełną listę obsługiwanych czasowników HTTP, zobacz HttpMethod. Aby uzyskać więcej informacji na temat wysyłania żądań HTTP, zobacz Wysyłanie żądania przy użyciu klienta HttpClient.

W poniższym przykładzie pokazano, jak wysłać żądanie 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();
}

W poprzednim kodzie CreateItemAsync metoda:

  • Serializuje Item parametr w formacie JSON przy użyciu polecenia System.Text.Json. Używa to wystąpienia JsonSerializerOptions klasy , aby skonfigurować proces serializacji.
  • Tworzy wystąpienie elementu StringContent w celu spakowania serializowanego kodu JSON do wysyłania w treści żądania HTTP.
  • Wywołuje metodę PostAsync wysyłania zawartości JSON do określonego adresu URL. Jest to względny adres URL, który jest dodawany do obiektu HttpClient.BaseAddress.
  • Wywołania EnsureSuccessStatusCode w celu zgłoszenia wyjątku, jeśli kod stanu odpowiedzi nie wskazuje powodzenia.

HttpClient obsługuje również inne typy zawartości. Przykład: MultipartContent i StreamContent. Aby uzyskać pełną listę obsługiwanych zawartości, zobacz HttpContent.

W poniższym przykładzie przedstawiono żądanie 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();
}

Powyższy kod jest bardzo podobny do przykładu POST . Metoda UpdateItemAsync wywołuje PutAsync metodę PostAsynczamiast .

W poniższym przykładzie przedstawiono żądanie HTTP DELETE :

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

    httpResponse.EnsureSuccessStatusCode();
}

W poprzednim kodzie metoda wywołuje metodę DeleteItemAsync DeleteAsync. Ponieważ żądania HTTP DELETE zwykle nie zawierają treści, DeleteAsync metoda nie udostępnia przeciążenia, które akceptuje wystąpienie HttpContentklasy .

Aby dowiedzieć się więcej na temat używania różnych czasowników HTTP w programie HttpClient, zobacz HttpClient.

HttpClient zarządzanie okresem istnienia

Nowe HttpClient wystąpienie jest zwracane za każdym razem, gdy CreateClient jest wywoływane w obiekcie IHttpClientFactory. Jedno HttpClientHandler wystąpienie jest tworzone na nazwę klienta. Fabryka zarządza okresami HttpClientHandler istnienia wystąpień.

IHttpClientFactory Buforuje HttpClientHandler wystąpienia utworzone przez fabrykę w celu zmniejszenia zużycia zasobów. Wystąpienie HttpClientHandler może być ponownie używane z pamięci podręcznej podczas tworzenia nowego HttpClient wystąpienia, jeśli jego okres istnienia nie wygasł.

Buforowanie programów obsługi jest pożądane, ponieważ każda procedura obsługi zwykle zarządza własną bazową pulą połączeń HTTP. Tworzenie większej liczby procedur obsługi niż jest to konieczne może spowodować wyczerpanie gniazd i opóźnienia połączeń. Niektóre programy obsługi utrzymują również połączenia otwarte na czas nieokreślony, co może uniemożliwić programowi obsługi reagowanie na zmiany DNS.

Domyślny okres istnienia programu obsługi to dwie minuty. Aby zastąpić wartość domyślną, wywołaj SetHandlerLifetime dla każdego klienta w pliku IServiceCollection:

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

Ważne

HttpClient wystąpienia utworzone przez IHttpClientFactory program mają być krótkotrwałe.

  • Odtwarzanie i ponowne tworzenie HttpMessageHandlerelementów po wygaśnięciu ich okresu istnienia jest niezbędne, IHttpClientFactory aby zapewnić, że programy obsługi reagują na zmiany DNS. HttpClient program jest powiązany z określonym wystąpieniem programu obsługi podczas jego tworzenia, dlatego należy zażądać nowych HttpClient wystąpień w odpowiednim czasie, aby upewnić się, że klient uzyska zaktualizowaną procedurę obsługi.

  • Usunięcie takich HttpClient wystąpień utworzonych przez fabrykę nie doprowadzi do wyczerpania gniazd, ponieważ jego usuwanie nie spowoduje usunięcia obiektu HttpMessageHandler. IHttpClientFactory śledzi i usuwa zasoby używane do tworzenia HttpClient wystąpień, w szczególności HttpMessageHandler wystąpień, gdy tylko ich okres istnienia wygaśnie i nie HttpClient będzie już ich używać.

Utrzymywanie pojedynczego HttpClient wystąpienia przy życiu przez długi czas jest typowym wzorcem, który może być używany jako alternatywa IHttpClientFactory dla programu , jednak ten wzorzec wymaga dodatkowej konfiguracji, takiej jak .PooledConnectionLifetime Można użyć klientów długotrwałych z PooledConnectionLifetimeklientami utworzonymi przez program lub krótkotrwałymi klientami utworzonymi przez IHttpClientFactoryprogram . Aby uzyskać informacje na temat strategii używanej w aplikacji, zobacz Wytyczne dotyczące korzystania z klientów HTTP.

Konfigurowanie HttpMessageHandler

Może być konieczne kontrolowanie konfiguracji wewnętrznej HttpMessageHandler używanej przez klienta.

Element IHttpClientBuilder jest zwracany podczas dodawania nazwanych lub wpisanych klientów. Metodę ConfigurePrimaryHttpMessageHandler rozszerzenia można użyć do zdefiniowania delegata w obiekcie IServiceCollection. Delegat służy do tworzenia i konfigurowania podstawowego HttpMessageHandler używanego przez tego klienta:

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

Skonfigurowanie programu HttClientHandler umożliwia określenie serwera proxy dla HttpClient wystąpienia między różnymi innymi właściwościami programu obsługi. Aby uzyskać więcej informacji, zobacz Serwer proxy na klienta.

Dodatkowa konfiguracja

Istnieje kilka dodatkowych opcji konfiguracji do kontrolowania elementu IHttpClientHandler:

Metoda opis
AddHttpMessageHandler Dodaje dodatkową procedurę obsługi komunikatów dla nazwanego HttpClientelementu .
AddTypedClient Konfiguruje powiązanie między elementem TClient i nazwanym HttpClient skojarzonym z elementem IHttpClientBuilder.
ConfigureHttpClient Dodaje delegata, który będzie używany do konfigurowania nazwanego HttpClientelementu .
ConfigurePrimaryHttpMessageHandler Konfiguruje element podstawowy HttpMessageHandler z kontenera wstrzykiwania zależności dla elementu o nazwie HttpClient.
RedactLoggedHeaders Ustawia kolekcję nazw nagłówków HTTP, dla których wartości powinny zostać zredagowane przed zalogowaniem.
SetHandlerLifetime Określa czas ponownego HttpMessageHandler użycia wystąpienia. Każdy nazwany klient może mieć własną skonfigurowaną wartość okresu istnienia programu obsługi.
UseSocketsHttpHandler Konfiguruje nowe lub wcześniej dodane SocketsHttpHandler wystąpienie z kontenera wstrzykiwania zależności do użycia jako podstawowy program obsługi dla nazwanego HttpClientelementu . (tylko platforma.NET 5 lub nowsza)

Używanie elementu IHttpClientFactory razem z aplikacją SocketsHttpHandler

Implementacja SocketsHttpHandler HttpMessageHandler programu została dodana w programie .NET Core 2.1, co umożliwia PooledConnectionLifetime skonfigurowanie. To ustawienie służy do zapewnienia, że program obsługi reaguje na zmiany DNS, więc użycie SocketsHttpHandler jest uważane za alternatywę dla używania programu IHttpClientFactory. Aby uzyskać więcej informacji, zobacz Wytyczne dotyczące korzystania z klientów HTTP.

SocketsHttpHandler IHttpClientFactory Można jednak używać ich razem w celu zwiększenia możliwości konfiguracji. Korzystając z obu tych interfejsów API, można korzystać z możliwości konfigurowania zarówno na niskim poziomie (na przykład w przypadku LocalCertificateSelectionCallback wyboru certyfikatu dynamicznego), jak i wysokiego poziomu (na przykład przy użyciu integracji di i kilku konfiguracji klientów).

Aby użyć obu interfejsów API:

  1. Określ SocketsHttpHandler jako PrimaryHandler za pośrednictwem , ConfigurePrimaryHttpMessageHandlerlub UseSocketsHttpHandler (tylko .NET 5+).
  2. Skonfiguruj na SocketsHttpHandler.PooledConnectionLifetime podstawie interwału, który ma zostać zaktualizowany przez system DNS, na przykład na wartość, która była wcześniej w HandlerLifetimepliku .
  3. (Opcjonalnie) Ponieważ SocketsHttpHandler będzie obsługiwać buforowanie połączeń i recykling, przetwarzanie IHttpClientFactory obsługi na poziomie nie jest już potrzebne. Można ją wyłączyć, ustawiając wartość HandlerLifetime Timeout.InfiniteTimeSpan.
services.AddHttpClient(name)
    .UseSocketsHttpHandler((handler, _) =>
        handler.PooledConnectionLifetime = TimeSpan.FromMinutes(2)) // Recreate connection every 2 minutes
    .SetHandlerLifetime(Timeout.InfiniteTimeSpan); // Disable rotation, as it is handled by PooledConnectionLifetime

W powyższym przykładzie do celów ilustracyjnych wybrano dowolnie 2 minuty, wyrównując do wartości domyślnej HandlerLifetime . Należy wybrać wartość na podstawie oczekiwanej częstotliwości zmian w systemie DNS lub innych zmianach sieci. Aby uzyskać więcej informacji, zobacz sekcję Zachowanie DNS w HttpClient wytycznych i sekcję Uwagi w dokumentacji interfejsu PooledConnectionLifetime API.

Unikaj wpisywanych klientów w usługach jednotonowych

W przypadku korzystania z nazwanego podejścia IHttpClientFactory klienta jest wstrzykiwany do usług, a HttpClient wystąpienia są tworzone przez wywołanie CreateClient za każdym razem, gdy HttpClient jest potrzebny.

Jednak w przypadku podejścia typizowanego klienta typizowane klienci są obiektami przejściowymi zwykle wstrzykiwanymi do usług. Może to spowodować problem, ponieważ typizowanego klienta można wstrzyknąć do pojedynczej usługi.

Ważne

Oczekuje się, że typowi klienci będą krótkotrwałi w tym samym sensie co HttpClient wystąpienia utworzone przez IHttpClientFactory (aby uzyskać więcej informacji, zobacz HttpClient zarządzanie okresem istnienia). Po utworzeniu IHttpClientFactory wpisanego wystąpienia klienta nie ma kontroli nad nim. Jeśli typizowane wystąpienie klienta jest przechwytywane w jednym miejscu, może to uniemożliwić reagowanie na zmiany DNS, pokonując jeden z celów IHttpClientFactoryprogramu .

Jeśli musisz używać HttpClient wystąpień w pojedynczej usłudze, rozważ następujące opcje:

  • Zamiast tego należy użyć nazwanego podejścia klienta , iniekcji IHttpClientFactory w pojedynczej usłudze i ponownego utworzenia HttpClient wystąpień w razie potrzeby.
  • Jeśli wymagane jest podejście typizowanego klienta , użyj polecenia SocketsHttpHandler z konfiguracją PooledConnectionLifetime jako podstawową procedurą obsługi. Aby uzyskać więcej informacji na temat używania z programem SocketsHttpHandler IHttpClientFactory, zobacz sekcję Using IHttpClientFactory razem z socketsHttpHandler.

Zakresy obsługi komunikatów w IHttpClientFactory

IHttpClientFactory Tworzy oddzielny zakres di dla każdego HttpMessageHandler wystąpienia. Te zakresy di są oddzielone od zakresów di aplikacji (na przykład ASP.NET zakresu żądań przychodzących lub zakresu ręcznego di utworzonego przez użytkownika), więc nie będą współużytkowane wystąpienia usługi o określonym zakresie. Zakresy obsługi komunikatów są powiązane z okresem istnienia programu obsługi i mogą przetrwać zakresy aplikacji, co może prowadzić na przykład do ponownego zastosowania tego samego wystąpienia z tymi samymi HttpMessageHandler zależnościami o określonym zakresie między kilkoma żądaniami przychodzącymi.

Diagram przedstawiający dwa zakresy di aplikacji i oddzielny zakres obsługi komunikatów

Użytkownicy zdecydowanie zaleca się, aby nie buforować informacji związanych z zakresem (takich jak dane z HttpContext) wewnątrz HttpMessageHandler wystąpień i używać zależności o określonym zakresie z ostrożnością, aby uniknąć wycieku poufnych informacji.

Jeśli potrzebujesz dostępu do zakresu di aplikacji z programu obsługi komunikatów, na potrzeby uwierzytelniania na przykład należy hermetyzować logikę rozpoznawania zakresu w osobnym przejściowym DelegatingHandlerelememencie i opakowować je wokół HttpMessageHandler wystąpienia z pamięci podręcznej IHttpClientFactory . Aby uzyskać dostęp do wywołania IHttpMessageHandlerFactory.CreateHandler programu obsługi dla dowolnego zarejestrowanego nazwanego klienta. W takim przypadku samodzielnie utworzysz HttpClient wystąpienie przy użyciu skonstruowanej procedury obsługi.

Diagram przedstawiający uzyskiwanie dostępu do zakresów di aplikacji za pośrednictwem oddzielnej procedury obsługi komunikatów przejściowych i IHttpMessageHandlerFactory

W poniższym przykładzie pokazano tworzenie obiektu HttpClient z rozpoznawaniem DelegatingHandlerzakresu:

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);

Kolejnym obejściem może być metoda rozszerzenia służąca do rejestrowania zakresu DelegatingHandler i zastępowania domyślnej IHttpClientFactory rejestracji przez usługę przejściową z dostępem do bieżącego zakresu aplikacji:

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;
}

Aby uzyskać więcej informacji, zobacz pełny przykład.

Zobacz też