IHttpClientFactory mit .NET
In diesem Artikel erfahren Sie, wie Sie mit der IHttpClientFactory
-Schnittstelle HttpClient
-Typen mit verschiedenen .NET-Grundlagen wie Dependency Injection (DI), Protokollierung und Konfiguration erstellen können. Der HttpClient-Typ wurde in .NET Framework 4.5 eingeführt, das 2012 veröffentlicht wurde. Anders ausgedrückt: Ihn gibt es schon eine Weile. HttpClient
wird zum Senden von HTTP-Anforderungen und zum Behandeln von HTTP-Antworten von Webressourcen verwendet, die von einem Uri identifiziert werden. Das HTTP-Protokoll macht den Großteil des Internetdatenverkehrs aus.
Mit modernen Anwendungsentwicklungsprinzipien, die bewährte Methoden fördern, dient IHttpClientFactory als Factoryabstraktion, die HttpClient
-Instanzen mit benutzerdefinierten Konfigurationen erstellen kann. IHttpClientFactory wurde in .NET Core 2.1 eingeführt. Gängige HTTP-basierte .NET-Workloads können die Resilienz und die Behandlung vorübergehender Fehler von Drittanbieter-Middleware problemlos nutzen.
Hinweis
Wenn Ihre App Cookies benötigt, ist es möglicherweise besser, die Verwendung von IHttpClientFactory in Ihrer App zu vermeiden. Alternative Methoden zum Verwalten von Clients finden Sie unter Richtlinien für die Verwendung von HTTP-Clients.
Wichtig
Die Lebensdauerverwaltung von HttpClient
-Instanzen, die von IHttpClientFactory
erstellt werden, unterscheidet sich vollständig von manuell erstellten Instanzen. Die Strategien sind die Verwendung kurzlebiger Clients, die von IHttpClientFactory
erstellt werden, oder langlebiger Clients, für die PooledConnectionLifetime
eingerichtet ist. Weitere Informationen finden Sie im Abschnitt HttpClient-Lebensdauerverwaltung und Richtlinien für die Verwendung von HTTP-Clients.
Der Typ IHttpClientFactory
Für den gesamten in diesem Artikel bereitgestellten Beispielquellcode ist die Installation des Microsoft.Extensions.Http
NuGet-Pakets erforderlich. Darüber hinaus veranschaulichen die Codebeispiele die Verwendung von HTTP-GET
Anforderungen zum Abrufen von BenutzerobjektenTodo
aus der kostenlosen {JSON}-Platzhalter-API.
Wenn Sie eine der AddHttpClient-Erweiterungsmethoden aufrufen, fügen Sie die IHttpClientFactory
und zugehörige Dienste der IServiceCollection hinzu. Der IHttpClientFactory
-Typ bietet folgende Vorteile:
- Er macht die
HttpClient
-Klasse als DI-bereiten Typ verfügbar. - Ein zentraler Ort für das Benennen und Konfigurieren logischer
HttpClient
-Instanzen wird damit geboten. - Das Konzept der ausgehenden Middleware wird über delegierende Handler in
HttpClient
in Code umgesetzt. - Außerdem werden Erweiterungsmethoden für auf Polly basierende Middleware bereitgestellt, um die delegierenden Handler in
HttpClient
zu nutzen. - Das Zwischenspeichern und die Lebensdauer von zugrunde liegenden HttpClientHandler-Instanzen werden verwaltet. Durch die automatische Verwaltung werden allgemeine DNS-Probleme (Domain Name System) vermieden, die bei der manuellen Verwaltung der Lebensdauer von
HttpClient
auftreten. - Eine konfigurierbare Protokollierungsfunktion wird (über ILogger) für alle Anforderungen hinzugefügt, die über Clients gesendet werden, die von der Factory erstellt wurden.
Verbrauchsmuster
Es gibt mehrere Möglichkeiten IHttpClientFactory
in einer App zu verwenden:
Der beste Ansatz richtet sich nach den Anforderungen der App.
Grundlegende Verwendung
Um die IHttpClientFactory
zu registrieren, rufen Sie den AddHttpClient
auf:
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();
Das Nutzen von Diensten kann die IHttpClientFactory
als Konstruktorparameter mit DI erfordern. Im folgenden Code wird IHttpClientFactory
verwendet, um eine HttpClient
-Instanz zu erstellen:
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 [];
}
}
IHttpClientFactory
wie im vorhergehenden Beispiel zu verwenden ist eine gute Möglichkeit zum Umgestalten einer vorhandene App. Dies hat keine Auswirkung auf die Verwendung von HttpClient
. An Stellen, an denen HttpClient
-Instanzen in einer vorhandenen App erstellt werden, können Sie diese Ereignisse mit Aufrufen von CreateClient ersetzen.
Benannte Clients
Benannte Clients sind in folgenden Fällen eine gute Wahl:
- Die App erfordert viele verschiedene Verwendungen von
HttpClient
. - Viele
HttpClient
-Instanzen haben unterschiedliche Konfigurationen.
Die Konfiguration eines benannten HttpClient
kann während der Registrierung in der IServiceCollection
angegeben werden:
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");
});
Im vorangehenden Code wird der Client mit Folgendem konfiguriert:
- Ein Name, der aus der Konfiguration unter dem
"TodoHttpClientName"
gezogen wird. - der Basisadresse
https://jsonplaceholder.typicode.com/
- Ein
"User-Agent"
-Header.
Sie können die Konfiguration verwenden, um HTTP-Clientnamen anzugeben. Dies ist hilfreich, um eine falsche Clientbenennung beim Hinzufügen und Erstellen zu vermeiden. In diesem Beispiel wird die Datei appsettings.json verwendet, um den HTTP-Clientnamen zu konfigurieren:
{
"TodoHttpClientName": "JsonPlaceholderApi"
}
Es ist einfach, diese Konfiguration zu erweitern und weitere Details zur gewünschten Funktionsweise Ihres HTTP-Clients zu speichern. Weitere Informationen finden Sie unter Konfiguration in .NET.
Erstellen des Clients
Jedes Mal, wenn CreateClient aufgerufen wird, geschieht Folgendes:
- Eine neue Instanz von
HttpClient
wird erstellt. - Die Konfigurationsaktion wird aufgerufen.
Übergeben Sie für die Erstellung eines benannten Clients dessen Namen an 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 [];
}
}
Im vorangehenden Code muss die HTTP-Anforderung keinen Hostnamen angeben. Der Code muss nur den Pfad übergeben, da die für den Client konfigurierte Basisadresse verwendet wird.
Typisierte Clients
Typisierte Clients:
- Stellen dieselben Funktionen wie benannte Clients bereit, ohne Zeichenfolgen als Schlüssel verwenden zu müssen.
- Bieten IntelliSense- und Compilerhilfe beim Nutzen von Clients.
- Stellen einen einzelnen Ort zum Konfigurieren und Interagieren mit einem bestimmten
HttpClient
bereit. Beispielsweise kann ein einzelner typisierter Client für Folgendes verwendet werden:- für einen einzelnen Back-End-Endpunkt
- um die gesamte Logik zu kapseln, die den Endpunkt behandelt
- Funktionieren mit DI und können überall in der App eingefügt werden, wo sie benötigt werden.
Ein typisierter Client akzeptiert einen HttpClient
-Parameter in seinem Konstruktor:
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();
}
Für den Code oben gilt:
- Die Konfiguration wird festgelegt, wenn der typisierte Client der Dienstsammlung hinzugefügt wird.
- Der
HttpClient
wird als Variable im Klassenbereich (Feld) zugewiesen und mit verfügbar gemachten APIs verwendet.
Es können API-spezifische Methoden erstellt werden, die die HttpClient
-Funktionalität verfügbar machen. Die GetUserTodosAsync
-Methode kapselt z. B. Code zum Abrufen von benutzerspezifischen Todo
-Objekten.
Der folgende Code ruft AddHttpClient auf, um eine typisierte Clientklasse zu registrieren:
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");
});
Der typisierte Client wird mit DI als „vorübergehend“ registriert. Im vorherigen Code registriert AddHttpClient
TodoService
als vorübergehenden Dienst. Diese Registrierung verwendet eine Factorymethode für folgende Aktionen:
- Erstellen Sie eine Instanz von
HttpClient
: - Erstellen einer
TodoService
-Instanz, wobei die Instanz vonHttpClient
an ihren Konstrukt übergeben wird
Wichtig
Die Verwendung von typisierten Clients in Singleton-Diensten kann gefährlich sein. Weitere Informationen finden Sie im Abschnitt Vermeiden von typisierten Clients in Singleton-Diensten.
Hinweis
Beim Registrieren eines typisierten Clients mit der AddHttpClient<TClient>
-Methode muss der TClient
-Typ über einen Konstruktor verfügen, der einen HttpClient
als Parameter akzeptiert. Darüber hinaus sollte der TClient
-Typ nicht separat mit dem DI-Container registriert werden, da dies dazu führt, dass die spätere Registrierung die frühere überschreibt.
Generierte Clients
IHttpClientFactory
kann in Verbindung mit Bibliotheken von Drittanbietern verwendet werden, z. B. Refit. Refit ist eine REST-Bibliothek für .NET. Sie ermöglicht deklarative REST-API-Definitionen, die Schnittstellenmethoden Endpunkten zuordnen. Eine Implementierung der Schnittstelle wird dynamisch von RestService
generiert. HttpClient
wird für die externen HTTP-Aufrufe verwendet.
Berücksichtigen Sie den folgenden record
-Typ:
namespace Shared;
public record class Todo(
int UserId,
int Id,
string Title,
bool Completed);
Das folgende Beispiel basiert auf dem Refit.HttpClientFactory
-NuGet-Paket und ist eine einfache Schnittstelle:
using Refit;
using Shared;
namespace GeneratedHttp.Example;
public interface ITodoService
{
[Get("/todos?userId={userId}")]
Task<Todo[]> GetUserTodosAsync(int userId);
}
Die vorangehende C#-Schnittstelle:
- Definiert eine Methode namens
GetUserTodosAsync
, die eineTask<Todo[]>
-Instanz zurückgibt. - Deklariert ein
Refit.GetAttribute
-Attribut mit dem Pfad und der Abfragezeichenfolge für die externe API.
Ein typisierter Client kann hinzugefügt werden. Refit wird zum Generieren der Implementierung verwendet:
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");
});
Die definierte Schnittstelle kann bei Bedarf mit der von DI und Refit bereitgestellten Implementierung verwendet werden.
Stellen von POST-, PUT- und DELETE-Anforderungen
In den vorangehenden Beispielen verwenden alle Anforderungen das GET
-HTTP-Verb. HttpClient
unterstützt ebenso andere HTTP-Verben, einschließlich der folgenden:
POST
PUT
DELETE
PATCH
Eine komplette Liste der unterstützten HTTP-Verben finden Sie unter HttpMethod. Weitere Informationen zum Senden von HTTP-Anforderungen finden Sie unter Senden einer Anforderung mit HttpClient.
Im folgenden Beispiel wird gezeigt, wie Sie eine HTTP-POST
-Anforderung vornehmen:
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();
}
Für die CreateItemAsync
-Methoden im Code oben gilt Folgendes:
- Sie serialisiert den
Item
-Parameter mithilfe vonSystem.Text.Json
in JSON. Dabei wird eine Instanz von JsonSerializerOptions verwenden, um den Serialisierungsprozess zu konfigurieren. - Sie erstellt eine Instanz von StringContent, um den serialisierten JSON-Code zum Senden im HTTP-Anforderungstext zu packen.
- Die Methode ruft PostAsync auf, um den JSON-Inhalt an die angegebene URL zu senden. Dies ist eine relative URL, die zu HttpClient.BaseAddress hinzugefügt wird.
- Sie ruft EnsureSuccessStatusCode auf, um eine Ausnahme auszulösen, wenn der Antwortstatuscode nicht auf Erfolg hinweist.
HttpClient
unterstützt auch andere Inhaltstypen. Beispiel: MultipartContent und StreamContent. Eine komplette Liste der unterstützten Inhaltstypen finden Sie unter HttpContent.
Das folgende Beispiel zeigt eine HTTP-PUT
-Anforderung:
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();
}
Der vorangehende Code ist dem POST
-Beispiel sehr ähnlich. Die UpdateItemAsync
-Methode ruft PutAsync anstelle von PostAsync
auf.
Das folgende Beispiel zeigt eine HTTP-DELETE
-Anforderung:
public async Task DeleteItemAsync(Guid id)
{
using HttpResponseMessage httpResponse =
await httpClient.DeleteAsync($"/api/items/{id}");
httpResponse.EnsureSuccessStatusCode();
}
Im vorangehenden Code ruft die DeleteItemAsync
-Methode DeleteAsync auf. Da HTTP-DELETE-Anforderungen normalerweise keinen Text enthalten, stellt die DeleteAsync
-Methode keine Überladung bereit, die eine Instanz von HttpContent
akzeptiert.
Weitere Informationen zur Verwendung unterschiedlicher HTTP-Verben mit HttpClient
finden Sie unter HttpClient.
Verwaltung der HttpClient
-Lebensdauer
Bei jedem Aufruf von CreateClient
in der IHttpClientFactory
wird eine neue Instanz von HttpClient
zurückgegeben. Pro Clientname wird eine HttpClientHandler-Instanz erstellt. Die Factory verwaltet die Lebensdauer der HttpClientHandler
-Instanzen.
IHttpClientFactory
nimmt die Zwischenspeicherung der HttpClientHandler
-Instanzen vor, die von der Factory zum Reduzieren des Ressourcenverbrauchs erstellt wurden. Eine HttpClientHandler
-Instanz kann aus dem Cache wiederverwendet werden, wenn eine neue HttpClient
-Instanz erstellt wird und deren Lebensdauer noch nicht abgelaufen ist.
Das Zwischenspeichern von Handlern ist ein wünschenswerter Vorgang, da jeder Handler in der Regel seinen zugrunde liegenden HTTP-Verbindungspool selbst verwaltet. Die Erstellung von mehr Handlern als nötig kann zu Socketerschöpfung und Verbindungsverzögerungen führen. Einige Handler halten Verbindungen auch unbegrenzt offen, was verhindert, dass der Handler auf DNS-Änderungen reagiert.
Die Standardlebensdauer von Handlern beträgt zwei Minuten. Um den Standardwert zu überschreiben, rufen Sie SetHandlerLifetime für jeden Client für die IServiceCollection
auf:
services.AddHttpClient("Named.Client")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
Wichtig
HttpClient
-Instanzen, die von IHttpClientFactory
erstellt werden, sollen kurzlebig sein.
Das Recyceln und erneute Erstellen von
HttpMessageHandler
n nach Ablauf ihrer Lebensdauer ist fürIHttpClientFactory
unerlässlich, um sicherzustellen, dass Handler auf DNS-Änderungen reagieren.HttpClient
ist bei der Erstellung an eine bestimmte Handlerinstanz gebunden, sodass neueHttpClient
-Instanzen rechtzeitig angefordert werden sollten, um sicherzustellen, dass der Client den aktualisierten Handler erhält.Die Entsorgung solcher
HttpClient
-Instanzen, die von der Factory erstellt wurden, führt nicht zu Socketerschöpfung, da ihre Entsorgung keine Entsorgung desHttpMessageHandler
auslöst.IHttpClientFactory
verfolgt Ressourcen nach und entsorgt diese, die zum Erstellen vonHttpClient
-Instanzen verwendet werden, insbesondere dieHttpMessageHandler
-Instanzen, sobald ihre Lebensdauer abläuft und sie nicht mehr von einemHttpClient
verwendet werden.
Eine einzelne HttpClient
-Instanz über einen langen Zeitraum am Leben zu halten, ist ein gängiges Muster, das als Alternative zu IHttpClientFactory
verwendet werden kann. Für dieses Muster ist jedoch zusätzliches Setup erforderlich, z. B. PooledConnectionLifetime
. Sie können entweder langlebige Clients mit PooledConnectionLifetime
oder kurzlebige Clients verwenden, die von IHttpClientFactory
erstellt werden. Informationen dazu, welche Strategie in Ihrer App verwendet werden sollte, finden Sie unter Richtlinien für die Verwendung von HTTP-Clients.
Konfigurieren des HttpMessageHandler
Es kann notwendig sein, die Konfiguration des inneren von einem Client verwendeten HttpMessageHandler zu steuern.
IHttpClientBuilder wird zurückgegeben, wenn benannte oder typisierte Clients hinzugefügt werden. Die Erweiterungsmethode ConfigurePrimaryHttpMessageHandler kann zum Definieren eines Delegaten für die IServiceCollection
verwendet werden. Der Delegat wird verwendet, um den primären HttpMessageHandler
zu erstellen und konfigurieren, der von dem Client verwendet wird:
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler
{
AllowAutoRedirect = false,
UseDefaultCredentials = true
};
});
Wenn Sie die HttClientHandler
konfigurieren, können Sie einen Proxy für die HttpClient
-Instanz unter verschiedenen anderen Eigenschaften des Handlers angeben. Weitere Informationen finden Sie unter Proxy pro Client.
Zusätzliche Konfiguration
Es gibt mehrere zusätzliche Konfigurationsoptionen zum Steuern von IHttpClientHandler
:
Methode | Beschreibung |
---|---|
AddHttpMessageHandler | Fügt einen zusätzlichen Meldungshandler für einen benannten HttpClient hinzu. |
AddTypedClient | Konfiguriert die Bindung zwischen dem TClient und dem benannten HttpClient , der der IHttpClientBuilder -Methode zugeordnet ist. |
ConfigureHttpClient | Fügt einen Delegaten hinzu, der für die Konfiguration einer benannten HttpClient -Klasse verwendet wird. |
ConfigurePrimaryHttpMessageHandler | Konfiguriert die primäre HttpMessageHandler -Klasse aus dem Abhängigkeitsinjektionscontainer für eine benannte HttpClient -Klasse. |
RedactLoggedHeaders | Legt die Sammlung von HTTP-Headernamen fest, für die vor der Protokollierung Werte bearbeitet werden sollen. |
SetHandlerLifetime | Legt die Zeitspanne fest, für die eine HttpMessageHandler -Klasse wiederverwendet werden kann. Für jeden benannten Client kann ein eigener Wert für die Lebensdauer des Handlers konfiguriert werden. |
UseSocketsHttpHandler | Konfiguriert eine neue oder eine zuvor hinzugefügte SocketsHttpHandler -Instanz aus dem Container zum Einfügen von Abhängigkeiten, die als primärer Handler für einen benannten HttpClient -Handler verwendet werden soll. (nur .NET 5+) |
Verwenden von IHttpClientFactory zusammen mit SocketsHttpHandler
Die SocketsHttpHandler
-Implementierung von HttpMessageHandler
wurde in .NET Core 2.1 hinzugefügt, wodurch PooledConnectionLifetime
konfiguriert werden kann. Diese Einstellung wird verwendet, um sicherzustellen, dass der Handler auf DNS-Änderungen reagiert. Daher wird die Verwendung von SocketsHttpHandler
als Alternative zur Verwendung von IHttpClientFactory
betrachtet. Weitere Informationen finden Sie unter Richtlinien für die Verwendung von HTTP-Clients.
SocketsHttpHandler
Allerdings können und IHttpClientFactory
zusammen verwendet werden, um die Konfigurierbarkeit zu verbessern. Durch die Verwendung dieser beiden APIs profitieren Sie von der Konfigurierbarkeit sowohl auf niedriger Ebene (z. B. bei der Verwendung von LocalCertificateSelectionCallback
für die Auswahl dynamischer Zertifikate) als auch auf hoher Ebene (z. B. durch Nutzung von DI-Integration und mehrerer Clientkonfigurationen).
So verwenden Sie beide APIs:
- Geben Sie
SocketsHttpHandler
alsPrimaryHandler
über ConfigurePrimaryHttpMessageHandler oder UseSocketsHttpHandler (nur .NET 5+). - Richten Sie SocketsHttpHandler.PooledConnectionLifetime basierend auf dem Intervall, in dem Sie erwarten, dass DNS aktualisiert wird; z. B. auf einen Wert, der zuvor in
HandlerLifetime
angegeben war. - (Optional) Da
SocketsHttpHandler
Verbindungspooling und Recycling verarbeitet, ist Handlerrecycling auf derIHttpClientFactory
-Ebene nicht mehr erforderlich. Sie können diese Einstellung deaktivieren, indem SieHandlerLifetime
aufTimeout.InfiniteTimeSpan
festlegen.
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
Im obigen Beispiel wurden zufallsweise 2 Minuten zur Veranschaulichung ausgewählt und an einen standardmäßigen HandlerLifetime
-Wert ausgerichtet. Sie sollten den Wert basierend auf der erwarteten Häufigkeit von DNS- oder anderen Netzwerkänderungen auswählen. Weitere Informationen finden Sie im Abschnitt DNS-Verhalten in den HttpClient
Richtlinien und im Abschnitt „Hinweise“ in der PooledConnectionLifetime API-Dokumentation.
Vermeiden von typisierten Clients in Singleton-Diensten
Bei Verwendung des Ansatzes mit benannten Clients wird IHttpClientFactory
in Dienste eingefügt, und HttpClient
-Instanzen werden erstellt, indem jedes Mal CreateClient aufgerufen wird, wenn ein HttpClient
benötigt wird.
Beim Ansatz mit typisierten Clients sind typisierte Clients jedoch vorübergehende Objekte, die normalerweise in Dienste eingefügt werden. Dies kann zu einem Problem führen, da ein typisierter Client in einen Singleton-Dienst eingefügt werden kann.
Wichtig
Von typisierten Clients wird erwartet, dass sie im gleichen Sinne kurzlebig sind wie HttpClient
-Instanzen, die von IHttpClientFactory
erstellt werden (weitere Informationen finden Sie unter HttpClient
-Lebensdauerverwaltung). Sobald eine typisierte Clientinstanz erstellt wird, besitzt IHttpClientFactory
keine Kontrolle über sie. Wenn eine typisierte Clientinstanz in einem Singleton erfasst wird, kann dies verhindern, dass auf DNS-Änderungen reagiert wird, was einen der Zwecke von IHttpClientFactory
vereitelt.
Wenn Sie HttpClient
-Instanzen in einem Singleton-Dienst verwenden müssen, sollten Sie die folgenden Optionen in Betracht ziehen:
- Verwenden Sie stattdessen den Ansatz mit einem benannten Client, indem Sie
IHttpClientFactory
in den Singleton-Dienst einfügen und bei BedarfHttpClient
-Instanzen neu erstellen. - Wenn Sie den Ansatz mit einem typisierten Client benötigen, verwenden Sie
SocketsHttpHandler
mit konfiguriertemPooledConnectionLifetime
-Wert als primären Handler. Weitere Informationen zur Verwendung vonSocketsHttpHandler
mitIHttpClientFactory
finden Sie im Abschnitt Verwenden von IHttpClientFactory zusammen mit SocketsHttpHandler.
Nachrichtenhandlerbereiche in IHttpClientFactory
IHttpClientFactory
erstellt für jede HttpMessageHandler
-Instanz einen separaten DI-Bereich. Diese DI-Bereiche sind von Anwendungs-DI-Bereichen getrennt (z. B. vom ASP.NET-Bereich für eingehende Anforderungen oder einem vom Benutzer erstellten manuellen DI-Bereich), sodass sie keine bereichsbezogenen Dienstinstanzen gemeinsam nutzen. Nachrichtenhandlerbereiche sind an die Lebensdauer des Handlers gebunden und können Anwendungsbereiche überdauern, was beispielsweise dazu führen kann, dass dieselbe HttpMessageHandler
-Instanz mit denselben eingefügten Bereichsabhängigkeiten zwischen mehreren eingehenden Anforderungen wiederverwendet wird.
Benutzern wird dringend empfohlen, bereichsbezogene Informationen (z. B. Daten von HttpContext
) nicht in HttpMessageHandler
-Instanzen zwischenzuspeichern und bereichsbezogene Abhängigkeiten mit Vorsicht zu verwenden, um zu vermeiden, dass vertrauliche Informationen veröffentlicht werden.
Wenn Sie von Ihrem Meldungshandler aus Zugriff auf einen App-DI-Bereich benötigen (beispielsweise zur Authentifizierung), kapseln Sie bereichsbezogene Logik in einem separaten vorübergehenden DelegatingHandler
, und umschließen ihn mit einer HttpMessageHandler
-Instanz aus dem IHttpClientFactory
-Cache. Um auf den Handler zuzugreifen, rufen Sie IHttpMessageHandlerFactory.CreateHandler für jeden registrierten benannten Client auf. In diesem Fall erstellen Sie eine HttpClient
-Instanz selbst mithilfe des erstellten Handlers.
Das folgende Beispiel zeigt das Erstellen eines HttpClient
mit einem bereichsbezogenen 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);
Eine weitere Problemumgehung kann mit einer Erweiterungsmethode für die Registrierung eines bereichsbezogenen DelegatingHandler
und einer IHttpClientFactory
-Standardregistrierung durch einen vorübergehenden Dienst mit Zugriff auf den aktuellen App-Bereich folgen:
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;
}
Weitere Informationen finden Sie im vollständigen Beispiel.
Siehe auch
- Häufige Probleme bei der Verwendung von
IHttpClientFactory
- Abhängigkeitsinjektion in .NET
- Protokollierung in .NET
- Konfiguration in .NET
- IHttpClientFactory
- IHttpMessageHandlerFactory
- HttpClient
- Vornehmen von HTTP-Anforderungen mit HttpClient
- Implementieren von HTTP-Wiederholungen mit exponentiellem Backoff