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
HttpClient
vereist. - Veel
HttpClient
exemplaren hebben verschillende configuraties.
Configuratie voor een benoemde HttpClient
kan worden opgegeven tijdens de registratie op het IServiceCollection
volgende:
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
HttpClient
locatie 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:
- Maak een instantie van
HttpClient
. - Maak een exemplaar van
TodoService
, door te geven in het exemplaar vanHttpClient
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 eenTask<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 vanSystem.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 IHttpClientFactory
wordt 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 IServiceCollection
volgende:
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
HttpMessageHandler
van 's wanneer hun levensduur verloopt, is essentieel omIHttpClientFactory
ervoor te zorgen dat de handlers reageren op DNS-wijzigingen.HttpClient
is gekoppeld aan een specifiek handlerexemplaren bij het maken ervan, zodat nieuweHttpClient
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 deHttpMessageHandler
.IHttpClientFactory
houdt resources bij die worden gebruikt voor het makenHttpClient
van exemplaren, met name de exemplaren, zodra hunHttpMessageHandler
levensduur verloopt en er geenHttpClient
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 PooledConnectionLifetime
een 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 HttpClient te configureren. |
ConfigurePrimaryHttpMessageHandler | Hiermee configureert u de primaire HttpMessageHandler van de container voor afhankelijkheidsinjectie voor een benoemde HttpClient naam. |
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:
- Geef
SocketsHttpHandler
op alsPrimaryHandler
via ConfigurePrimaryHttpMessageHandler, of UseSocketsHttpHandler (alleen.NET 5+ ). - 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
. - (Optioneel) Aangezien
SocketsHttpHandler
verbindingspooling en recycling worden verwerkt, is de handlerrecycling op hetIHttpClientFactory
niveau niet meer nodig. U kunt dit uitschakelen door deze in te stellenHandlerLifetime
opTimeout.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 opnieuwHttpClient
maakt. - Als u de getypte clientbenadering nodig hebt, gebruikt
SocketsHttpHandler
u deze met geconfigureerdPooledConnectionLifetime
als een primaire handler. Zie de sectie IHttpClientFactory samen met SocketsHttpHandler gebruiken voor meer informatie over het gebruikSocketsHttpHandler
IHttpClientFactory
met.
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.
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 DelegatingHandler
logica 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.
In het volgende voorbeeld ziet u hoe u een HttpClient
met een bereikbewuste DelegatingHandler
maakt:
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.