In modernen ASP.NET-Anwendungen – insbesondere mit der Einführung von Minimal APIs in .NET 9 – ist die Konfiguration von HTTPClients für externe Aufrufe sowohl leistungsstark als auch flexibel. Eine der wichtigsten Stärken ist die Möglichkeit, benannte HTTPClients zu erstellen und deren Verhalten durch Message-Handler zu erweitern. In diesem Beitrag untersuchen wir zwei Arten von Message-Handlern:
- PrimaryMessageHandlers: Ermöglichen die Kontrolle über den zugrunde liegenden Transport (z. B. Aktivierung von TLS1.3, Anpassung der SSL-Zertifikatsvalidierung).
- DelegatingHandlers: Erlauben das Modifizieren oder Überprüfen von HTTP-Anfragen und -Antworten auf Anwendungsebene (z. B. Festlegen der HTTP-Version, Logging, benutzerdefinierte Header).
Nachfolgend zeigen wir, wie jede dieser Methoden in Minimal API ASP.NET-Diensten eingesetzt werden kann, begleitet von Codebeispielen.
Benannte HTTPClients in Minimal API-Diensten
Die Abhängigkeitsinjektion von ASP.NET macht es einfach, benannte HTTPClients zu registrieren. Verschiedene Clients können mit unterschiedlichen Konfigurationen versehen und später über IHttpClientFactory
abgerufen werden.
Beispiel für die Registrierung:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient("MyClient");
// Weitere benannte Clients hinzufügen
var app = builder.Build();
app.MapGet("/", async (IHttpClientFactory clientFactory) =>
{
var client = clientFactory.CreateClient("MyClient");
var response = await client.GetAsync("https://example.com");
return await response.Content.ReadAsStringAsync();
});
app.Run();
PrimaryMessageHandlers: Anpassungen auf Transportebene
Der PrimaryMessageHandler ist der zugrunde liegende Handler, der sich um den Netzwerktransport kümmert. Durch seine Konfiguration können Details wie TLS-Protokolle und die Validierung von SSL-Zertifikaten angepasst werden. Dies ist besonders nützlich, wenn bestimmte Sicherheitsanforderungen durchgesetzt werden müssen.
Beispiel: Konfiguration von TLS1.3 und benutzerdefinierter SSL-Zertifikatsvalidierung
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient("MyPrimaryClient")
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
// Erzwinge TLS1.3 für sichere Kommunikation
EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls13,
RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{
// Benutzerdefinierte Logik zur Validierung von SSL-Zertifikaten
Console.WriteLine("Primary handler: Validierung des SSL-Zertifikats.");
return true; // Oder eine richtige Validierungslogik implementieren
}
}
});
// Weitere benannte Clients konfigurieren
var app = builder.Build();
app.MapGet("/primary", async (IHttpClientFactory clientFactory) =>
{
var client = clientFactory.CreateClient("MyPrimaryClient");
var response = await client.GetAsync("https://example.com");
return await response.Content.ReadAsStringAsync();
});
app.Run();
DelegatingHandlers: Anpassungen auf Anwendungsebene
DelegatingHandlers befinden sich in der HTTPClient-Pipeline und ermöglichen das Modifizieren von Anfragen oder Antworten. Sie eignen sich ideal für Aufgaben wie Logging, das Hinzufügen benutzerdefinierter Header oder das Erzwingen bestimmter Protokolle wie HTTP/2.
Beispiel: Benutzerdefinierter DelegatingHandler zur Erzwingung von HTTP/2
public class CustomDelegatingHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Erzwinge HTTP/2 für ausgehende Anfragen
request.Version = HttpVersion.Version20;
Console.WriteLine("CustomDelegatingHandler: Anfrage wurde auf HTTP/2 gesetzt.");
return await base.SendAsync(request, cancellationToken);
}
}
Lektion gelernt
Der Versuch, die schlanke modulare Struktur von DelegatingHandlers zu nutzen, um Transportebenen-Konfigurationen anzuwenden, funktioniert nicht. Das Modifizieren des InnerHandler eines DelegatingHandler führt zu einer Ausnahme:
System.InvalidOperationException: Die ‘InnerHandler’-Eigenschaft muss null sein. ‘DelegatingHandler’-Instanzen, die ‘HttpMessageHandlerBuilder’ übergeben werden, dürfen nicht wiederverwendet oder zwischengespeichert werden.
Dies liegt daran, dass das Modifizieren des InnerHandler die Handler-Pipeline unterbrechen würde. Ein Umgehen der Ausnahme durch das Umhüllen jeder Anfrage mit einem neuen HttpMessageInvoker würde zwar funktionieren, aber den Verarbeitungsfluss unterbrechen.
Fazit: Verwende DelegatingHandlers nicht für Transportebenen-Anpassungen.
Registrierung des Handlers mit einem benannten HTTPClient
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTransient<CustomDelegatingHandler>();
builder.Services.AddHttpClient("MyDelegatingClient")
.AddHttpMessageHandler<CustomDelegatingHandler>();
var app = builder.Build();
app.MapGet("/delegating", async (IHttpClientFactory clientFactory) =>
{
var client = clientFactory.CreateClient("MyDelegatingClient");
var response = await client.GetAsync("https://example.com");
return await response.Content.ReadAsStringAsync();
});
app.Run();
Fazit
Durch die Kombination von DelegatingHandlers und PrimaryMessageHandlers kannst du die HTTP-Kommunikation in deinen ASP.NET Core Minimal API-Diensten präzise steuern:
- Benannte HTTP-Clients bieten eine modulare Möglichkeit zur Konfiguration von HTTP-Clients für verschiedene Ziele.
- PrimaryMessageHandlers ermöglichen Anpassungen auf Transportebene (z. B. Erzwingen von TLS1.3, Verwaltung der SSL-Zertifikatsvalidierung).
- DelegatingHandlers erlauben Anpassungen auf Anwendungsebene (z. B. Erzwingen von HTTP/2, Hinzufügen von Headern).
Dieser mehrschichtige Ansatz ermöglicht robuste, sichere und flexible HTTP-Interaktionen, die speziell auf die Anforderungen deiner Anwendung zugeschnitten sind. Happy Coding!