Delen via


IHttpClientFactory met .NET

In dit artikel leert u hoe u de IHttpClientFactory interface gebruikt om typen te maken HttpClient met verschillende .NET-basisprincipes, zoals afhankelijkheidsinjectie (DI), logboekregistratie en configuratie. Het HttpClient type is geïntroduceerd in .NET Framework 4.5, dat in 2012 werd uitgebracht. Met andere woorden, het is al een tijdje rond. HttpClient wordt gebruikt voor het maken van HTTP-aanvragen en het verwerken van HTTP-antwoorden van webresources die worden geïdentificeerd door een Uri. Het HTTP-protocol vormt de overgrote meerderheid van al het internetverkeer.

Met moderne toepassingsontwikkelingsprincipes die de best practices stimuleren, fungeert het IHttpClientFactory als een factoryabstractie waarmee exemplaren met aangepaste configuraties kunnen worden gemaakt HttpClient . IHttpClientFactory is geïntroduceerd in .NET Core 2.1. Veelvoorkomende .NET-workloads op basis van HTTP kunnen eenvoudig profiteren van tolerante en tijdelijke foutafhandeling van middleware van derden.

Notitie

Als uw app cookies vereist, is het misschien beter om het gebruik IHttpClientFactory in uw app te voorkomen. Zie Richtlijnen voor het gebruik van HTTP-clients voor alternatieve manieren om clients te beheren.

Belangrijk

Levensduurbeheer van HttpClient exemplaren die door IHttpClientFactory zijn gemaakt, verschilt volledig van exemplaren die handmatig zijn gemaakt. De strategieën zijn het gebruik van clients met een korte levensduur die zijn gemaakt door IHttpClientFactory of langlevende clients die zijn PooledConnectionLifetime ingesteld. Zie de sectie Levensduurbeheer van HttpClient en richtlijnen voor het gebruik van HTTP-clients voor meer informatie.

Het IHttpClientFactory type

Voor alle voorbeeldbroncode in dit artikel is de installatie van het Microsoft.Extensions.Http NuGet-pakket vereist. Bovendien laten de codevoorbeelden zien hoe HTTP-aanvragen GET worden gebruikt om gebruikersobjecten Todo op te halen uit de gratis tijdelijke aanduidingen-API voor {JSON}.

Wanneer u een van de AddHttpClient extensiemethoden aanroept, voegt u de IHttpClientFactory en gerelateerde services toe aan de IServiceCollection. Het IHttpClientFactory type biedt de volgende voordelen:

  • HttpClient De klasse wordt weergegeven als een type di-ready.
  • Biedt een centrale locatie voor het benoemen en configureren van logische HttpClient exemplaren.
  • Codifeert het concept van uitgaande middleware via het delegeren van handlers in HttpClient.
  • Biedt uitbreidingsmethoden voor op Polly gebaseerde middleware om te profiteren van het delegeren van handlers in HttpClient.
  • Beheert de cache en levensduur van onderliggende HttpClientHandler exemplaren. Automatisch beheer voorkomt veelvoorkomende DNS-problemen (Domain Name System) die optreden bij het handmatig beheren van HttpClient de levensduur.
  • Voegt een configureerbare logboekregistratie-ervaring (via ILogger) toe voor alle aanvragen die worden verzonden via clients die door de fabriek zijn gemaakt.

Consumptiepatronen

Er zijn verschillende manieren IHttpClientFactory om in een app te gebruiken:

De beste aanpak is afhankelijk van de vereisten van de app.

Basaal gebruik

Als u de IHttpClientFactory, oproep 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();

Het gebruik van services kan de IHttpClientFactory parameter als constructor met DI vereisen. De volgende code gebruikt IHttpClientFactory om een HttpClient exemplaar te maken:

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

Het gebruik van IHttpClientFactory zoals in het voorgaande voorbeeld is een goede manier om een bestaande app te herstructureren. Het heeft geen invloed op hoe HttpClient wordt gebruikt. Op plaatsen waar HttpClient exemplaren worden gemaakt in een bestaande app, vervangt u deze exemplaren door aanroepen naar CreateClient.

Benoemde clients

Benoemde clients zijn een goede keuze wanneer:

  • Voor de app zijn veel verschillende toepassingen van HttpClientvereist.
  • Veel HttpClient exemplaren hebben verschillende configuraties.

Configuratie voor een benoemde HttpClient kan worden opgegeven tijdens de registratie op het IServiceCollectionvolgende:

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

In de voorgaande code is de client geconfigureerd met:

  • Een naam die wordt opgehaald uit de configuratie onder de "TodoHttpClientName".
  • Het basisadres https://jsonplaceholder.typicode.com/.
  • Een "User-Agent" koptekst.

U kunt de configuratie gebruiken om HTTP-clientnamen op te geven. Dit is handig om te voorkomen dat clients de naam verkeerd noemen wanneer u deze toevoegt en maakt. In dit voorbeeld wordt het appsettings.json-bestand gebruikt om de HTTP-clientnaam te configureren:

{
    "TodoHttpClientName": "JsonPlaceholderApi"
}

Het is eenvoudig om deze configuratie uit te breiden en meer informatie op te slaan over hoe u uw HTTP-client wilt laten functioneren. Zie Configuratie in .NET voor meer informatie.

Client maken

Elke keer CreateClient wordt het volgende aangeroepen:

  • Er wordt een nieuw exemplaar gemaakt HttpClient .
  • De configuratieactie wordt aangeroepen.

Als u een benoemde client wilt maken, geeft u de naam door in 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 [];
    }
}

In de voorgaande code hoeft de HTTP-aanvraag geen hostnaam op te geven. De code kan alleen het pad doorgeven, omdat het basisadres dat is geconfigureerd voor de client wordt gebruikt.

Getypte clients

Getypte clients:

  • Bied dezelfde mogelijkheden als benoemde clients zonder dat u tekenreeksen als sleutels hoeft te gebruiken.
  • Geef IntelliSense en compiler hulp bij het verbruik van clients.
  • Geef één locatie op om een bepaalde HttpClientlocatie te configureren en ermee te communiceren. Er kan bijvoorbeeld één getypeerde client worden gebruikt:
    • Voor één back-endeindpunt.
    • Als u alle logica voor het eindpunt wilt inkapselen.
  • Werk met DI en kan waar nodig worden geïnjecteerd in de app.

Een getypte client accepteert een HttpClient parameter in de constructor:

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

In de voorgaande code:

  • De configuratie wordt ingesteld wanneer de getypte client wordt toegevoegd aan de serviceverzameling.
  • Deze HttpClient wordt toegewezen als een variabele met klassebereik (veld) en wordt gebruikt met weergegeven API's.

API-specifieke methoden kunnen worden gemaakt die functionaliteit beschikbaar HttpClient maken. Met de GetUserTodosAsync methode wordt bijvoorbeeld code ingekapseld om gebruikersspecifieke Todo objecten op te halen.

Met de volgende code wordt AddHttpClient een getypte clientklasse geregistreerd:

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

De getypte client wordt geregistreerd als tijdelijk bij DI. In de voorgaande code AddHttpClient wordt geregistreerd TodoService als een tijdelijke service. Deze registratie maakt gebruik van een factory-methode voor het volgende:

  1. Maak een instantie van HttpClient.
  2. Maak een exemplaar van TodoService, door te geven in het exemplaar van HttpClient de constructor.

Belangrijk

Het gebruik van getypte clients in singleton-services kan gevaarlijk zijn. Zie de sectie Getypte clients vermijden in singleton-services voor meer informatie.

Notitie

Wanneer u een getypte client registreert bij de AddHttpClient<TClient> methode, moet het TClient type een constructor hebben die een HttpClient als parameter accepteert. Daarnaast mag het TClient type niet afzonderlijk worden geregistreerd bij de DI-container, omdat dit leidt tot de latere registratie die de vorige overschrijft.

Gegenereerde clients

IHttpClientFactory kan worden gebruikt in combinatie met bibliotheken van derden, zoals Refit. Refit is een REST-bibliotheek voor .NET. Hiermee kunt u declaratieve REST API-definities, interfacemethoden toewijzen aan eindpunten. Er wordt dynamisch een implementatie van de interface gegenereerd door de RestService, waarbij HttpClient de externe HTTP-aanroepen worden uitgevoerd.

Houd rekening met het volgende record type:

namespace Shared;

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

Het volgende voorbeeld is afhankelijk van het Refit.HttpClientFactory NuGet-pakket en is een eenvoudige interface:

using Refit;
using Shared;

namespace GeneratedHttp.Example;

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

De voorgaande C#-interface:

  • Hiermee definieert u een methode met de naam GetUserTodosAsync die een Task<Todo[]> exemplaar retourneert.
  • Declareert een Refit.GetAttribute kenmerk met het pad en de querytekenreeks naar de externe API.

Een getypte client kan worden toegevoegd met Behulp van Refit om de implementatie te genereren:

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

De gedefinieerde interface kan waar nodig worden gebruikt, met de implementatie van DI en Refit.

POST-, PUT- en DELETE-aanvragen maken

In de voorgaande voorbeelden gebruiken alle HTTP-aanvragen het GET HTTP-werkwoord. HttpClient ondersteunt ook andere HTTP-woorden, waaronder:

  • POST
  • PUT
  • DELETE
  • PATCH

Zie voor een volledige lijst met ondersteunde HTTP-woorden HttpMethod. Zie Een aanvraag verzenden met httpClient voor meer informatie over het maken van HTTP-aanvragen.

In het volgende voorbeeld ziet u hoe u een HTTP-aanvraag POST maakt:

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

In de voorgaande code is de CreateItemAsync methode:

  • Serialiseert de Item parameter naar JSON met behulp van System.Text.Json. Dit maakt gebruik van een exemplaar van JsonSerializerOptions het serialisatieproces te configureren.
  • Hiermee maakt u een exemplaar van StringContent het pakket van de geserialiseerde JSON voor verzending in de hoofdtekst van de HTTP-aanvraag.
  • Aanroepen PostAsync om de JSON-inhoud naar de opgegeven URL te verzenden. Dit is een relatieve URL die wordt toegevoegd aan het HttpClient.BaseAddress.
  • Aanroepen EnsureSuccessStatusCode om een uitzondering te genereren als de antwoordstatuscode niet aangeeft dat deze is geslaagd.

HttpClient ondersteunt ook andere typen inhoud. Bijvoorbeeld MultipartContent en StreamContent. Zie voor een volledige lijst met ondersteunde inhoud HttpContent.

In het volgende voorbeeld ziet u een HTTP-aanvraag 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();
}

De voorgaande code is vergelijkbaar met het POST voorbeeld. De UpdateItemAsync methode roept PutAsync in plaats van PostAsync.

In het volgende voorbeeld ziet u een HTTP-aanvraag DELETE :

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

    httpResponse.EnsureSuccessStatusCode();
}

In de voorgaande code roept DeleteAsyncde methode aanDeleteItemAsync. Omdat HTTP DELETE-aanvragen doorgaans geen hoofdtekst bevatten, biedt de DeleteAsync methode geen overbelasting die een exemplaar accepteert.HttpContent

Zie HttpClientvoor meer informatie over het gebruik van verschillende HTTP-woorden met HttpClient.

HttpClient levensduurbeheer

Er wordt telkens een nieuw HttpClient exemplaar geretourneerd wanneer CreateClient deze IHttpClientFactorywordt aangeroepen. Er wordt één HttpClientHandler exemplaar gemaakt per clientnaam. De fabriek beheert de levensduur van de HttpClientHandler exemplaren.

IHttpClientFactory slaat de HttpClientHandler exemplaren op die door de factory zijn gemaakt om het resourceverbruik te verminderen. Een HttpClientHandler exemplaar kan opnieuw worden gebruikt vanuit de cache bij het maken van een nieuw HttpClient exemplaar als de levensduur ervan niet is verlopen.

Caching van handlers is wenselijk omdat elke handler doorgaans een eigen onderliggende HTTP-verbindingsgroep beheert. Het maken van meer handlers dan nodig kan leiden tot socketuitputting en verbindingsvertragingen. Sommige handlers houden verbindingen ook voor onbepaalde tijd open, waardoor de handler niet kan reageren op DNS-wijzigingen.

De standaardlevensduur van de handler is twee minuten. Als u de standaardwaarde wilt overschrijven, roept u SetHandlerLifetime aan voor elke client op het IServiceCollectionvolgende:

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

Belangrijk

HttpClient exemplaren die door IHttpClientFactory zijn gemaakt, zijn bedoeld om kort te leven.

  • Recycling en recreatie HttpMessageHandlervan 's wanneer hun levensduur verloopt, is essentieel om IHttpClientFactory ervoor te zorgen dat de handlers reageren op DNS-wijzigingen. HttpClient is gekoppeld aan een specifiek handlerexemplaren bij het maken ervan, zodat nieuwe HttpClient exemplaren tijdig moeten worden aangevraagd om ervoor te zorgen dat de client de bijgewerkte handler krijgt.

  • Het verwijderen van dergelijke HttpClient exemplaren die door de fabriek zijn gemaakt, zal niet leiden tot uitputting van de socket, omdat de verwijdering niet leidt tot verwijdering van de HttpMessageHandler. IHttpClientFactory houdt resources bij die worden gebruikt voor het maken HttpClient van exemplaren, met name de exemplaren, zodra hun HttpMessageHandler levensduur verloopt en er geen HttpClient resources meer worden gebruikt.

Het actief houden van één HttpClient exemplaar voor een lange duur is een gemeenschappelijk patroon dat kan worden gebruikt als alternatief IHttpClientFactory voor, maar dit patroon vereist extra instellingen, zoals PooledConnectionLifetime. U kunt clients met een lange levensduur of clients met PooledConnectionLifetimeeen korte levensduur gebruiken die zijn gemaakt door IHttpClientFactory. Zie Richtlijnen voor het gebruik van HTTP-clients voor informatie over welke strategie u in uw app wilt gebruiken.

Configureer de HttpMessageHandler

Het kan nodig zijn om de configuratie van de binnenste HttpMessageHandler te beheren die door een client wordt gebruikt.

Er IHttpClientBuilder wordt een geretourneerd bij het toevoegen van benoemde of getypte clients. De ConfigurePrimaryHttpMessageHandler extensiemethode kan worden gebruikt voor het definiëren van een gemachtigde op de IServiceCollection. De gemachtigde wordt gebruikt voor het maken en configureren van de primaire die HttpMessageHandler door die client wordt gebruikt:

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

Als u de HttClientHandler mogelijkheden configureert, kunt u een proxy voor het HttpClient exemplaar opgeven onder verschillende andere eigenschappen van de handler. Zie Proxy per client voor meer informatie.

Aanvullende configuratie

Er zijn verschillende aanvullende configuratieopties voor het beheren van:IHttpClientHandler

Wijze Description
AddHttpMessageHandler Voegt een extra berichthandler toe voor een benoemde HttpClient.
AddTypedClient Hiermee configureert u de binding tussen de TClient en de benoemde die HttpClient is gekoppeld aan de IHttpClientBuilder.
ConfigureHttpClient Hiermee voegt u een gemachtigde toe die wordt gebruikt om een benoemde naam HttpClientte configureren.
ConfigurePrimaryHttpMessageHandler Hiermee configureert u de primaire HttpMessageHandler van de container voor afhankelijkheidsinjectie voor een benoemde HttpClientnaam.
RedactLoggedHeaders Hiermee stelt u de verzameling HTTP-headernamen in waarvoor waarden moeten worden bewerkt voordat u zich aanmeldt.
SetHandlerLifetime Hiermee stelt u de tijdsduur in die een HttpMessageHandler exemplaar opnieuw kan worden gebruikt. Elke benoemde client kan een eigen geconfigureerde handler-levensduurwaarde hebben.
UseSocketsHttpHandler Hiermee configureert u een nieuw of een eerder toegevoegd SocketsHttpHandler exemplaar van de container voor afhankelijkheidsinjectie om te worden gebruikt als een primaire handler voor een benoemde HttpClient. (.NET 5+ alleen)

IHttpClientFactory gebruiken in combinatie met SocketsHttpHandler

De SocketsHttpHandler implementatie van HttpMessageHandler is toegevoegd in .NET Core 2.1, die kan PooledConnectionLifetime worden geconfigureerd. Deze instelling wordt gebruikt om ervoor te zorgen dat de handler reageert op DNS-wijzigingen, dus het gebruik SocketsHttpHandler wordt beschouwd als een alternatief voor het gebruik IHttpClientFactory. Zie Richtlijnen voor het gebruik van HTTP-clients voor meer informatie.

SocketsHttpHandler En IHttpClientFactory kan echter samen worden gebruikt om de configureerbaarheid te verbeteren. Door beide API's te gebruiken, profiteert u van de configureerbaarheid op zowel een laag niveau (bijvoorbeeld voor LocalCertificateSelectionCallback dynamische certificaatselectie) als een hoog niveau (bijvoorbeeld het gebruik van DI-integratie en verschillende clientconfiguraties).

Beide API's gebruiken:

  1. Geef SocketsHttpHandler op als PrimaryHandler via ConfigurePrimaryHttpMessageHandler, of UseSocketsHttpHandler (alleen.NET 5+ ).
  2. Stel deze in SocketsHttpHandler.PooledConnectionLifetime op basis van het interval dat u verwacht dat DNS wordt bijgewerkt, bijvoorbeeld naar een waarde die zich eerder bevond HandlerLifetime.
  3. (Optioneel) Aangezien SocketsHttpHandler verbindingspooling en recycling worden verwerkt, is de handlerrecycling op het IHttpClientFactory niveau niet meer nodig. U kunt dit uitschakelen door deze in te stellen HandlerLifetime op 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

In het bovenstaande voorbeeld zijn 2 minuten willekeurig gekozen voor illustratiedoeleinden, waarbij een standaardwaarde HandlerLifetime wordt uitgelijnd. Kies de waarde op basis van de verwachte frequentie van DNS- of andere netwerkwijzigingen. Zie de sectie DNS-gedrag in de HttpClient richtlijnen en de sectie Opmerkingen in de PooledConnectionLifetime API-documentatie voor meer informatie.

Getypte clients in singleton-services voorkomen

Wanneer u de benoemde clientbenadering gebruikt, IHttpClientFactory wordt deze opgenomen in services en HttpClient worden exemplaren gemaakt door telkens aan te roepen CreateClient wanneer een HttpClient client nodig is.

Met de getypte clientbenadering zijn getypte clients echter tijdelijke objecten die meestal in services worden geïnjecteerd. Dit kan een probleem veroorzaken omdat een getypte client kan worden geïnjecteerd in een singleton-service.

Belangrijk

Getypte clients worden naar verwachting in dezelfde zin HttpClient als exemplaren die zijn IHttpClientFactory gemaakt door (zie levensduurbeheer) voor meer informatieHttpClient. Zodra er een getypt clientexemplaren zijn gemaakt, IHttpClientFactory heeft u er geen controle over. Als een getypt clientexemplaren in een singleton worden vastgelegd, kan het voorkomen dat het reageert op DNS-wijzigingen, waardoor een van de doeleinden van IHttpClientFactory.

Als u exemplaren in een singleton-service wilt gebruiken HttpClient , kunt u de volgende opties overwegen:

  • Gebruik in plaats daarvan de benoemde clientbenadering , waarbij u in de singleton-service injecteert IHttpClientFactory en instanties indien nodig opnieuw HttpClient maakt.
  • Als u de getypte clientbenadering nodig hebt, gebruikt SocketsHttpHandler u deze met geconfigureerd PooledConnectionLifetime als een primaire handler. Zie de sectie IHttpClientFactory samen met SocketsHttpHandler gebruiken voor meer informatie over het gebruik SocketsHttpHandler IHttpClientFactorymet.

Berichthandlerbereiken in IHttpClientFactory

IHttpClientFactory maakt een afzonderlijk DI-bereik per instantie HttpMessageHandler . Deze DI-bereiken staan los van toepassings-DI-bereiken (bijvoorbeeld ASP.NET binnenkomende aanvraagbereik of een handmatig DI-bereik dat door de gebruiker is gemaakt), zodat ze geen scoped service-exemplaren delen. Berichtenhandlerbereiken zijn gekoppeld aan de levensduur van de handler en kunnen toepassingsbereiken overleven. Dit kan bijvoorbeeld leiden tot het hergebruik van hetzelfde HttpMessageHandler exemplaar met dezelfde geïnjecteerde afhankelijkheden binnenkomende aanvragen.

Diagram met twee di-bereiken van toepassingen en een afzonderlijk berichthandlerbereik

Gebruikers raden u ten zeer zeer aan om bereikgerelateerde informatie (zoals gegevens uit HttpContext) niet in de cache op te cachen binnen HttpMessageHandler exemplaren en om voorzichtig bereikafhankelijkheden te gebruiken om te voorkomen dat gevoelige informatie wordt gelekt.

Als u toegang tot een di-bereik van een app vanuit uw berichtenhandler nodig hebt, kunt u voor verificatie bijvoorbeeld bereikbewuste logica in een afzonderlijke tijdelijke DelegatingHandlerlogica inkapselen en deze verpakken rond een HttpMessageHandler exemplaar uit de IHttpClientFactory cache. Voor toegang tot de handler-aanroep IHttpMessageHandlerFactory.CreateHandler voor een geregistreerde benoemde client. In dat geval maakt u zelf een HttpClient exemplaar met behulp van de samengestelde handler.

Diagram van het verkrijgen van toegang tot di-bereiken van apps via een afzonderlijke tijdelijke berichthandler en IHttpMessageHandlerFactory

In het volgende voorbeeld ziet u hoe u een HttpClient met een bereikbewuste DelegatingHandlermaakt:

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

Een verdere tijdelijke oplossing kan worden gevolgd door een uitbreidingsmethode voor het registreren van een bereikbewuste DelegatingHandler en het overschrijven van standaardregistratie IHttpClientFactory door een tijdelijke service met toegang tot het huidige app-bereik:

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

Zie het volledige voorbeeld voor meer informatie.

Zie ook