Ereignisse
Power BI DataViz Weltmeisterschaften
14. Feb., 16 Uhr - 31. März, 16 Uhr
Mit 4 Chancen, ein Konferenzpaket zu gewinnen und es zum LIVE Grand Finale in Las Vegas zu machen
Weitere InformationenDieser Browser wird nicht mehr unterstützt.
Führen Sie ein Upgrade auf Microsoft Edge durch, um die neuesten Features, Sicherheitsupdates und den technischen Support zu nutzen.
Hinweis
Dies ist nicht die neueste Version dieses Artikels. Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.
Warnung
Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie in der .NET- und .NET Core-Supportrichtlinie. Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.
Wichtig
Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.
Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.
Von Kirk Larkin, Steve Gordon, Glenn Condron und Ryan Nowak.
IHttpClientFactory kann registriert und zum Konfigurieren und Erstellen von HttpClient-Instanzen in einer App verwendet werden. IHttpClientFactory
bietet die folgenden Vorteile:
HttpClient
-Instanzen wird damit geboten. Beispielsweise kann ein Client namens github registriert und für den Zugriff auf GitHub konfiguriert werden. Ein Standardclient kann für den allgemeinen Zugriff registriert werden.HttpClient
in Code umgesetzt. Außerdem werden Erweiterungen für auf Polly basierende Middleware bereitgestellt, um die delegierenden Handler in HttpClient
zu nutzen.HttpClientMessageHandler
-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.ILogger
) für alle Anforderungen hinzugefügt, die über Clients gesendet werden, die von der Factory erstellt wurden.Der Beispielcode in dieser Version des Themas verwendet System.Text.Json, um JSON-Inhalte zu deserialisieren, die in HTTP-Antworten zurückgegeben wurden. Verwenden Sie bei Beispielen, die Json.NET
und ReadAsAsync<T>
verwenden, die Versionsauswahl, um eine 2.x-Version dieses Themas auszuwählen.
Es gibt mehrere Möglichkeiten IHttpClientFactory
in einer App zu verwenden:
Der beste Ansatz richtet sich nach den Anforderungen der App.
Registrieren Sie IHttpClientFactory
, indem Sie AddHttpClient
in Program.cs
aufrufen:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddHttpClient();
Eine IHttpClientFactory
-Schnittstelle kann mithilfe der Dependency Injection (DI) angefordert werden. Im folgenden Code wird IHttpClientFactory
verwendet, um eine HttpClient
-Instanz zu erstellen:
public class BasicModel : PageModel
{
private readonly IHttpClientFactory _httpClientFactory;
public BasicModel(IHttpClientFactory httpClientFactory) =>
_httpClientFactory = httpClientFactory;
public IEnumerable<GitHubBranch>? GitHubBranches { get; set; }
public async Task OnGet()
{
var httpRequestMessage = new HttpRequestMessage(
HttpMethod.Get,
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches")
{
Headers =
{
{ HeaderNames.Accept, "application/vnd.github.v3+json" },
{ HeaderNames.UserAgent, "HttpRequestsSample" }
}
};
var httpClient = _httpClientFactory.CreateClient();
var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);
if (httpResponseMessage.IsSuccessStatusCode)
{
using var contentStream =
await httpResponseMessage.Content.ReadAsStreamAsync();
GitHubBranches = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(contentStream);
}
}
}
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 sind in folgenden Fällen eine gute Wahl:
HttpClient
.HttpClient
s verfügen über unterschiedliche Konfigurationen.Geben Sie die Konfiguration für einen benannten HttpClient
bei dessen Registrierung in Program.cs
an:
builder.Services.AddHttpClient("GitHub", httpClient =>
{
httpClient.BaseAddress = new Uri("https://api.github.com/");
// using Microsoft.Net.Http.Headers;
// The GitHub API requires two headers.
httpClient.DefaultRequestHeaders.Add(
HeaderNames.Accept, "application/vnd.github.v3+json");
httpClient.DefaultRequestHeaders.Add(
HeaderNames.UserAgent, "HttpRequestsSample");
});
Im vorangehenden Code wird der Client mit Folgendem konfiguriert:
https://api.github.com/
Jedes Mal, wenn CreateClient aufgerufen wird, geschieht Folgendes:
HttpClient
wird erstellt.Übergeben Sie für die Erstellung eines benannten Clients dessen Namen an CreateClient
:
public class NamedClientModel : PageModel
{
private readonly IHttpClientFactory _httpClientFactory;
public NamedClientModel(IHttpClientFactory httpClientFactory) =>
_httpClientFactory = httpClientFactory;
public IEnumerable<GitHubBranch>? GitHubBranches { get; set; }
public async Task OnGet()
{
var httpClient = _httpClientFactory.CreateClient("GitHub");
var httpResponseMessage = await httpClient.GetAsync(
"repos/dotnet/AspNetCore.Docs/branches");
if (httpResponseMessage.IsSuccessStatusCode)
{
using var contentStream =
await httpResponseMessage.Content.ReadAsStreamAsync();
GitHubBranches = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(contentStream);
}
}
}
Im vorangehenden Code muss die Anforderung keinen Hostnamen angeben. Der Code muss nur den Pfad übergeben, da die für den Client konfigurierte Basisadresse verwendet wird.
Typisierte Clients:
HttpClient
bereit. Beispielsweise kann ein einzelner typisierter Client für Folgendes verwendet werden: Ein typisierter Client akzeptiert einen HttpClient
-Parameter in seinem Konstruktor:
public class GitHubService
{
private readonly HttpClient _httpClient;
public GitHubService(HttpClient httpClient)
{
_httpClient = httpClient;
_httpClient.BaseAddress = new Uri("https://api.github.com/");
// using Microsoft.Net.Http.Headers;
// The GitHub API requires two headers.
_httpClient.DefaultRequestHeaders.Add(
HeaderNames.Accept, "application/vnd.github.v3+json");
_httpClient.DefaultRequestHeaders.Add(
HeaderNames.UserAgent, "HttpRequestsSample");
}
public async Task<IEnumerable<GitHubBranch>?> GetAspNetCoreDocsBranchesAsync() =>
await _httpClient.GetFromJsonAsync<IEnumerable<GitHubBranch>>(
"repos/dotnet/AspNetCore.Docs/branches");
}
Für den Code oben gilt:
HttpClient
-Instanz wird als privates Feld gespeichert.Es können API-spezifische Methoden erstellt werden, die die HttpClient
-Funktionalität verfügbar machen. Die GetAspNetCoreDocsBranches
-Methode kapselt z. B. Code zum Abrufen von GitHub-Branches mit Dokumentation.
Der folgende Code ruft AddHttpClient in Program.cs
auf, um die typisierte GitHubService
-Clientklasse zu registrieren:
builder.Services.AddHttpClient<GitHubService>();
Der typisierte Client wird mit DI als „vorübergehend“ registriert. Im vorherigen Code registriert AddHttpClient
GitHubService
als vorübergehenden Dienst. Diese Registrierung verwendet eine Factorymethode für folgende Aktionen:
HttpClient
:GitHubService
-Instanz, wobei die Instanz von HttpClient
an ihren Konstrukt übergeben wirdDer typisierte Client kann direkt eingefügt und verwendet werden:
public class TypedClientModel : PageModel
{
private readonly GitHubService _gitHubService;
public TypedClientModel(GitHubService gitHubService) =>
_gitHubService = gitHubService;
public IEnumerable<GitHubBranch>? GitHubBranches { get; set; }
public async Task OnGet()
{
try
{
GitHubBranches = await _gitHubService.GetAspNetCoreDocsBranchesAsync();
}
catch (HttpRequestException)
{
// ...
}
}
}
Die Konfiguration kann für einen typisierten Client auch während dessen Registrierung in Program.cs
angegeben werden, anstatt im Konstruktor des typisierten Clients:
builder.Services.AddHttpClient<GitHubService>(httpClient =>
{
httpClient.BaseAddress = new Uri("https://api.github.com/");
// ...
});
IHttpClientFactory
kann in Verbindung mit Bibliotheken von Drittanbietern verwendet werden, z. B. Refit. Refit ist eine REST-Bibliothek für .NET. Sie konvertiert REST-APIs in Live-Schnittstellen. Rufen Sie AddRefitClient
auf, um eine dynamische Implementierung einer Schnittstelle zu generieren, die HttpClient
zum Durchführen der externen HTTP-Aufrufe verwendet.
Eine benutzerdefinierte Schnittstelle stellt die externe API dar:
public interface IGitHubClient
{
[Get("/repos/dotnet/AspNetCore.Docs/branches")]
Task<IEnumerable<GitHubBranch>> GetAspNetCoreDocsBranchesAsync();
}
Rufen Sie AddRefitClient
auf, um die dynamische Implementierung zu generieren, und rufen Sie dann ConfigureHttpClient
auf, um den zugrunde liegenden HttpClient
zu konfigurieren:
builder.Services.AddRefitClient<IGitHubClient>()
.ConfigureHttpClient(httpClient =>
{
httpClient.BaseAddress = new Uri("https://api.github.com/");
// using Microsoft.Net.Http.Headers;
// The GitHub API requires two headers.
httpClient.DefaultRequestHeaders.Add(
HeaderNames.Accept, "application/vnd.github.v3+json");
httpClient.DefaultRequestHeaders.Add(
HeaderNames.UserAgent, "HttpRequestsSample");
});
Verwenden Sie DI, um auf die dynamische Implementierung von IGitHubClient
zuzugreifen:
public class RefitModel : PageModel
{
private readonly IGitHubClient _gitHubClient;
public RefitModel(IGitHubClient gitHubClient) =>
_gitHubClient = gitHubClient;
public IEnumerable<GitHubBranch>? GitHubBranches { get; set; }
public async Task OnGet()
{
try
{
GitHubBranches = await _gitHubClient.GetAspNetCoreDocsBranchesAsync();
}
catch (ApiException)
{
// ...
}
}
}
In den vorangehenden Beispielen verwenden alle Anforderungen das GET-HTTP-Verb. HttpClient
unterstützt ebenso andere HTTP-Verben, einschließlich der folgenden:
Eine komplette Liste der unterstützten HTTP-Verben finden Sie unter HttpMethod.
Im folgenden Beispiel wird gezeigt, wie Sie eine HTTP-POST-Anforderung stellen:
public async Task CreateItemAsync(TodoItem todoItem)
{
var todoItemJson = new StringContent(
JsonSerializer.Serialize(todoItem),
Encoding.UTF8,
Application.Json); // using static System.Net.Mime.MediaTypeNames;
using var httpResponseMessage =
await _httpClient.PostAsync("/api/TodoItems", todoItemJson);
httpResponseMessage.EnsureSuccessStatusCode();
}
Für die CreateItemAsync
-Methoden im Code oben gilt Folgendes:
TodoItem
-Parameter mithilfe von System.Text.Json
in JSON.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 SaveItemAsync(TodoItem todoItem)
{
var todoItemJson = new StringContent(
JsonSerializer.Serialize(todoItem),
Encoding.UTF8,
Application.Json);
using var httpResponseMessage =
await _httpClient.PutAsync($"/api/TodoItems/{todoItem.Id}", todoItemJson);
httpResponseMessage.EnsureSuccessStatusCode();
}
Der vorangehende Code ist dem POST-Beispiel ähnlich. Die SaveItemAsync
-Methode ruft PutAsync anstelle von PostAsync
auf.
Das folgende Beispiel zeigt eine HTTP-DELETE-Anforderung.
public async Task DeleteItemAsync(long itemId)
{
using var httpResponseMessage =
await _httpClient.DeleteAsync($"/api/TodoItems/{itemId}");
httpResponseMessage.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.
HttpClient
enthält das Konzept, Handler zu delegieren, die für ausgehende HTTP-Anforderungen miteinander verknüpft werden können. IHttpClientFactory
:
So erstellen Sie einen delegierenden Handler:
public class ValidateHeaderHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
if (!request.Headers.Contains("X-API-KEY"))
{
return new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(
"The API key header X-API-KEY is required.")
};
}
return await base.SendAsync(request, cancellationToken);
}
}
Der vorangehende Code überprüft, ob die Anforderung einen X-API-KEY
-Header enthält. Wenn X-API-KEY
fehlt, wird BadRequest zurückgegeben.
Für eine HttpClient
-Klasse mit Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandler kann mehr als ein Handler zur Konfiguration hinzugefügt werden:
builder.Services.AddTransient<ValidateHeaderHandler>();
builder.Services.AddHttpClient("HttpMessageHandler")
.AddHttpMessageHandler<ValidateHeaderHandler>();
Im vorangehenden Code ist ValidateHeaderHandler
mit DI registriert. Nach der Registrierung kann AddHttpMessageHandler aufgerufen werden, was den Typ für den Handler übergibt.
Mehrere Handler können in der Reihenfolge registriert werden, in der sie ausgeführt werden sollen. Jeder Handler umschließt den nächsten Handler, bis der endgültige HttpClientHandler
die Anforderung ausführt:
builder.Services.AddTransient<SampleHandler1>();
builder.Services.AddTransient<SampleHandler2>();
builder.Services.AddHttpClient("MultipleHttpMessageHandlers")
.AddHttpMessageHandler<SampleHandler1>()
.AddHttpMessageHandler<SampleHandler2>();
Im vorangehenden Code wird SampleHandler1
zuerst ausgeführt, vor SampleHandler2
.
Wenn IHttpClientFactory
einen neuen delegierenden Handler erstellt, wird DI verwendet, um die Konstruktorparameter des Handlers zu erfüllen. IHttpClientFactory
erstellt einen separaten DI-Bereich für jeden Handler. Dies kann zu überraschendem Verhalten führen, wenn ein Handler einen bereichsbezogenen Dienst nutzt.
Sehen Sie sich beispielsweise die folgende Schnittstelle und ihre Implementierung an, die eine Aufgabe als Vorgang mit einem Bezeichner OperationId
darstellt:
public interface IOperationScoped
{
string OperationId { get; }
}
public class OperationScoped : IOperationScoped
{
public string OperationId { get; } = Guid.NewGuid().ToString()[^4..];
}
Wie der Name bereits vermuten lässt, wird IOperationScoped
bei DI registriert, wobei eine bereichsbezogene Lebensdauer verwendet wird:
builder.Services.AddScoped<IOperationScoped, OperationScoped>();
Der folgende delegierende Handler verwendet IOperationScoped
, um den X-OPERATION-ID
-Header für die ausgehende Anforderung festzulegen:
public class OperationHandler : DelegatingHandler
{
private readonly IOperationScoped _operationScoped;
public OperationHandler(IOperationScoped operationScoped) =>
_operationScoped = operationScoped;
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("X-OPERATION-ID", _operationScoped.OperationId);
return await base.SendAsync(request, cancellationToken);
}
}
Navigieren Sie im HttpRequestsSample
-Download zu /Operation
, und aktualisieren Sie die Seite. Der Wert für den Anforderungsbereich ändert sich für jede Anforderung, aber der Wert des Handlerbereichs ändert sich nur alle 5 Sekunden.
Handler können von Diensten eines beliebigen Bereichs abhängen. Dienste, von denen Handler abhängig sind, werden verworfen, wenn der Handler verworfen wird.
Verwenden Sie einen der folgenden Ansätze, um den anforderungsspezifischen Zustand mit Meldungshandlern zu teilen:
IHttpClientFactory
ist mit der Drittanbieterbibliothek Polly integriert. Polly ist eine umfassende Bibliothek für die Behandlung von beständigen und vorübergehenden Fehlern für .NET. Entwicklern wird damit ermöglicht, Richtlinien wie Wiederholungsrichtlinien, Trennschalterrichtlinien, Timeout-Richtlinien, Bulkhead Isolation-Richtlinien und Ausweichrichtlinien in einer flüssigen und threadsicheren Art und Weise auszudrücken.
Erweiterungsmethoden werden bereitgestellt, um die Verwendung von Polly-Richtlinien mit konfigurierten HttpClient
-Instanzen zu aktivieren. Die Polly-Erweiterungen unterstützen das Hinzufügen von Polly-basierten Handlern zu Clients. Polly erfordert das Microsoft.Extensions.Http.Polly-NuGet-Paket.
Fehler treten normalerweise auf, wenn externe HTTP-Aufrufe vorübergehend sind. AddTransientHttpErrorPolicy ermöglicht die Definition einer Richtlinie, um vorübergehende Fehler zu behandeln. Mit AddTransientHttpErrorPolicy
konfigurierte Richtlinien behandeln die folgenden Antworten:
AddTransientHttpErrorPolicy
ermöglicht den Zugriff auf ein PolicyBuilder
-Objekt, das für die Fehlerbehandlung konfiguriert wurde und mögliche vorübergehende Fehler darstellt:
builder.Services.AddHttpClient("PollyWaitAndRetry")
.AddTransientHttpErrorPolicy(policyBuilder =>
policyBuilder.WaitAndRetryAsync(
3, retryNumber => TimeSpan.FromMilliseconds(600)));
Im vorangehenden Code wird eine WaitAndRetryAsync
-Richtlinie definiert. Anforderungsfehler werden bis zu dreimal mit einer Verzögerung von 600 ms wiederholt.
Erweiterungsmethoden werden zum Hinzufügen von Polly-basierten Handlern bereitgestellt, z. B. AddPolicyHandler. Der folgende AddPolicyHandler
-Überladung prüft die Anforderung, um zu entscheiden, welche Richtlinie angewendet werden soll:
var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(10));
var longTimeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(30));
builder.Services.AddHttpClient("PollyDynamic")
.AddPolicyHandler(httpRequestMessage =>
httpRequestMessage.Method == HttpMethod.Get ? timeoutPolicy : longTimeoutPolicy);
Im vorangehenden Code wird ein 10 Sekunden langer Timeout angewendet, wenn die ausgehende Anforderung eine HTTP GET-Anforderung ist. Für alle anderen HTTP-Methoden wird ein 30 Sekunden langer Timeout verwendet.
Es ist üblich, Polly-Richtlinien zu schachteln:
builder.Services.AddHttpClient("PollyMultiple")
.AddTransientHttpErrorPolicy(policyBuilder =>
policyBuilder.RetryAsync(3))
.AddTransientHttpErrorPolicy(policyBuilder =>
policyBuilder.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
Im vorherigen Beispiel:
AddTransientHttpErrorPolicy
-Aufruf fügt eine Trennschalterrichtlinie hinzu. Weitere externe Anforderungen werden 30 Sekunden lang blockiert, wenn fünf Fehlversuche hintereinander stattfinden. Trennschalterrichtlinien sind zustandsbehaftet. Alle Aufrufe über diesen Client teilen den gleichen Schalterzustand.Eine Methode zum Verwalten regelmäßig genutzter Richtlinien ist, Sie einmal zu definieren und mit PolicyRegistry
zu registrieren. Beispiel:
var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(10));
var longTimeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(30));
var policyRegistry = builder.Services.AddPolicyRegistry();
policyRegistry.Add("Regular", timeoutPolicy);
policyRegistry.Add("Long", longTimeoutPolicy);
builder.Services.AddHttpClient("PollyRegistryRegular")
.AddPolicyHandlerFromRegistry("Regular");
builder.Services.AddHttpClient("PollyRegistryLong")
.AddPolicyHandlerFromRegistry("Long");
Für den Code oben gilt:
Regular
und Long
.Weitere Informationen zu IHttpClientFactory
und Polly-Integrationen finden Sie im Polly-Wiki.
Bei jedem Aufruf von CreateClient
in der IHttpClientFactory
wird eine neue Instanz von HttpClient
zurückgegeben. Pro benannter Client wird ein HttpMessageHandler erstellt. Die Factory verwaltet die Lebensdauer der HttpMessageHandler
-Instanzen.
IHttpClientFactory
legt die HttpMessageHandler
-Instanzen zusammen, die von der Factory zum Reduzieren des Ressourcenverbrauchs erstellt wurden. Eine HttpMessageHandler
-Instanz kann aus dem Pool wiederverwendet werden, wenn eine neue HttpClient
-Instanz erstellt wird und deren Lebensdauer noch nicht abgelaufen ist.
Das Zusammenlegen von Handlern ist ein wünschenswerter Vorgang, da jeder Handler in der Regel seine zugrunde liegenden HTTP-Verbindungen selbst verwaltet. Wenn mehr Handler als nötig erstellt werden, können Verzögerungen bei Verbindungen entstehen. Einige Handler halten Verbindungen auch unbegrenzt offen, was verhindert, dass der Handler auf DNS-Änderungen (Domain Name System) reagiert.
Die Standardlebensdauer von Handlern beträgt zwei Minuten. Der Standardwert kann für jeden benannten Client überschrieben werden:
builder.Services.AddHttpClient("HandlerLifetime")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
HttpClient
-Instanzen können im Allgemeinen als .NET-Objekte behandelt werden, die nicht verworfen werden müssen. Beim Verwerfen werden ausgehende Anforderungen abgebrochen, und es wird sichergestellt, dass die angegebene HttpClient
-Instanz nach dem Aufruf von Dispose nicht mehr verwendet werden kann. IHttpClientFactory
verfolgt von HttpClient
-Instanzen verwendete Ressourcen nach und verwirft sie.
Das Beibehalten einer einzelnen HttpClient
-Instanz für einen langen Zeitraum ist ein allgemeines Muster, das vor der Einführung von IHttpClientFactory
verwendet wurde. Dieses Muster wird nach der Migration zu IHttpClientFactory
überflüssig.
Mit der Verwendung von IHttpClientFactory
in einer DI-fähigen App wird Folgendes vermieden:
HttpMessageHandler
-InstanzenHttpMessageHandler
-InstanzenEs gibt alternative Möglichkeiten zum Lösen des vorangehenden Problems mithilfe einer langlebigen SocketsHttpHandler-Instanz:
SocketsHttpHandler
-Instanz, wenn die App gestartet wird, und verwenden Sie diese für die Lebensdauer der App.HttpClient
-Instanzen mithilfe von new HttpClient(handler, disposeHandler: false)
.Diese Ansätze lösen die Ressourcenverwaltungsprobleme, die IHttpClientFactory
auf ähnliche Weise löst.
SocketsHttpHandler
gibt Verbindungen für HttpClient
-Instanzen frei. Durch diese Freigabe wird die Erschöpfung von Sockets vermieden.SocketsHttpHandler
wechselt die Verbindungen anhand von PooledConnectionLifetime
, um Probleme durch veraltetes DNS zu umgehen.Über IHttpClientFactory
erstellte Clients zeichnen Protokollmeldungen für alle Anforderungen auf. Aktivieren Sie die entsprechende Informationsebene in der Protokollierungskonfiguration, um die Standardprotokollmeldungen anzuzeigen. Zusätzliche Protokollierung, z.B. das Protokollieren von Anforderungsheadern, wird nur auf der Ablaufverfolgungsebene enthalten.
Die Protokollierungskategorie für jeden Client enthält den Namen des Clients. Ein Client namens MyNamedClient protokolliert z. B. Nachrichten mit der Kategorie „System.Net.Http.HttpClient.MyNamedClient.LogicalHandler“. Meldungen mit dem Suffix LogicalHandler treten außerhalb der Anforderungshandlerpipeline auf. In der Anforderung werden Meldungen protokolliert, bevor andere Handler in der Pipeline sie verarbeitet haben. In der Antwort werden Meldungen protokolliert, nachdem andere Handler in der Pipeline die Antwort empfangen haben.
Die Protokollierung tritt ebenfalls innerhalb der Anforderungshandlerpipeline auf. Im Beispiel von MyNamedClient werden diese Nachrichten mit der Protokollkategorie „System.Net.Http.HttpClient.MyNamedClient.ClientHandler“ protokolliert. Für die Anforderung erfolgt dies, nachdem alle anderen Handler ausgeführt wurden und bevor die Anforderung gesendet wird. Diese Protokollierung enthält den Zustand der Antwort in der Antwort, bevor sie an die Handlerpipeline zurückgegeben wird.
Das Aktivieren der Protokollierung außerhalb und innerhalb der Pipeline ermöglicht die Überprüfung der Änderungen, die durch andere Handler in der Pipeline erfolgt sind. Dies kann die Änderungen an Anforderungsheadern oder am Statuscode der Antwort enthalten.
Das Einschließen des Namens des Clients in der Protokollkategorie aktiviert die Protokollfilterung für spezifische benannte Clients.
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 verwendet werden. Der Delegat wird verwendet, um den primären HttpMessageHandler
zu erstellen und konfigurieren, der von dem Client verwendet wird:
builder.Services.AddHttpClient("ConfiguredHttpMessageHandler")
.ConfigurePrimaryHttpMessageHandler(() =>
new HttpClientHandler
{
AllowAutoRedirect = true,
UseDefaultCredentials = true
});
Die zusammengelegten HttpMessageHandler
-Instanzen resultieren in der Freigabe von CookieContainer
-Objekten. Die unerwartete Freigabe von CookieContainer
-Objekten führt oft zu fehlerhaftem Code. Ziehen Sie für Apps, die Cookies erfordern, folgende Vorgehensweisen in Betracht:
IHttpClientFactory
Rufen Sie ConfigurePrimaryHttpMessageHandler auf, um die automatische cookieverarbeitung zu deaktivieren:
builder.Services.AddHttpClient("NoAutomaticCookies")
.ConfigurePrimaryHttpMessageHandler(() =>
new HttpClientHandler
{
UseCookies = false
});
Fügen Sie in einer Konsolen-App dem Projekt die folgenden Paketverweise hinzu:
Im folgenden Beispiel:
GitHubService
sind im Dienstcontainer des generischen Hosts registriert.GitHubService
wird über DI angefordert, die wiederum eine Instanz von IHttpClientFactory
anfordert.GitHubService
erstellt über IHttpClientFactory
eine Instanz von HttpClient
, die verwendet wird, um GitHub-Branches mit Dokumentation abzurufen.using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
var host = new HostBuilder()
.ConfigureServices(services =>
{
services.AddHttpClient();
services.AddTransient<GitHubService>();
})
.Build();
try
{
var gitHubService = host.Services.GetRequiredService<GitHubService>();
var gitHubBranches = await gitHubService.GetAspNetCoreDocsBranchesAsync();
Console.WriteLine($"{gitHubBranches?.Count() ?? 0} GitHub Branches");
if (gitHubBranches is not null)
{
foreach (var gitHubBranch in gitHubBranches)
{
Console.WriteLine($"- {gitHubBranch.Name}");
}
}
}
catch (Exception ex)
{
host.Services.GetRequiredService<ILogger<Program>>()
.LogError(ex, "Unable to load branches from GitHub.");
}
public class GitHubService
{
private readonly IHttpClientFactory _httpClientFactory;
public GitHubService(IHttpClientFactory httpClientFactory) =>
_httpClientFactory = httpClientFactory;
public async Task<IEnumerable<GitHubBranch>?> GetAspNetCoreDocsBranchesAsync()
{
var httpRequestMessage = new HttpRequestMessage(
HttpMethod.Get,
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches")
{
Headers =
{
{ "Accept", "application/vnd.github.v3+json" },
{ "User-Agent", "HttpRequestsConsoleSample" }
}
};
var httpClient = _httpClientFactory.CreateClient();
var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);
httpResponseMessage.EnsureSuccessStatusCode();
using var contentStream =
await httpResponseMessage.Content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(contentStream);
}
}
public record GitHubBranch(
[property: JsonPropertyName("name")] string Name);
Für die Headerweitergabe wird eine ASP.NET Core-Middleware verwendet, um HTTP-Header von eingehenden Anforderungen an ausgehende HttpClient
-Anforderungen weiterzugeben. So verwenden Sie die Headerweitergabe:
Installieren Sie das Paket Microsoft.AspNetCore.HeaderPropagation.
Konfigurieren Sie den HttpClient
und die Middlewarepipeline in Program.cs
:
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddHttpClient("PropagateHeaders")
.AddHeaderPropagation();
builder.Services.AddHeaderPropagation(options =>
{
options.Headers.Add("X-TraceId");
});
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.UseHeaderPropagation();
app.MapControllers();
Erstellen Sie ausgehende Anforderungen mithilfe der konfigurierten HttpClient
-Instanz, die die hinzugefügten Header enthält.
Von Kirk Larkin, Steve Gordon, Glenn Condron und Ryan Nowak.
IHttpClientFactory kann registriert und zum Konfigurieren und Erstellen von HttpClient-Instanzen in einer App verwendet werden. IHttpClientFactory
bietet die folgenden Vorteile:
HttpClient
-Instanzen wird damit geboten. Beispielsweise kann ein Client namens github registriert und für den Zugriff auf GitHub konfiguriert werden. Ein Standardclient kann für den allgemeinen Zugriff registriert werden.HttpClient
in Code umgesetzt. Außerdem werden Erweiterungen für auf Polly basierende Middleware bereitgestellt, um die delegierenden Handler in HttpClient
zu nutzen.HttpClientMessageHandler
-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.ILogger
) für alle Anforderungen hinzugefügt, die über Clients gesendet werden, die von der Factory erstellt wurden.Zeigen Sie Beispielcode an, oder laden Sie diesen herunter (Vorgehensweise zum Herunterladen).
Der Beispielcode in dieser Version des Themas verwendet System.Text.Json, um JSON-Inhalte zu deserialisieren, die in HTTP-Antworten zurückgegeben wurden. Verwenden Sie bei Beispielen, die Json.NET
und ReadAsAsync<T>
verwenden, die Versionsauswahl, um eine 2.x-Version dieses Themas auszuwählen.
Es gibt mehrere Möglichkeiten IHttpClientFactory
in einer App zu verwenden:
Der beste Ansatz richtet sich nach den Anforderungen der App.
IHttpClientFactory
kann durch Aufrufen von AddHttpClient
registriert werden:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
// Remaining code deleted for brevity.
Eine IHttpClientFactory
-Schnittstelle kann mithilfe der Dependency Injection (DI) angefordert werden. Im folgenden Code wird IHttpClientFactory
verwendet, um eine HttpClient
-Instanz zu erstellen:
public class BasicUsageModel : PageModel
{
private readonly IHttpClientFactory _clientFactory;
public IEnumerable<GitHubBranch> Branches { get; private set; }
public bool GetBranchesError { get; private set; }
public BasicUsageModel(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task OnGet()
{
var request = new HttpRequestMessage(HttpMethod.Get,
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches");
request.Headers.Add("Accept", "application/vnd.github.v3+json");
request.Headers.Add("User-Agent", "HttpClientFactory-Sample");
var client = _clientFactory.CreateClient();
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
Branches = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(responseStream);
}
else
{
GetBranchesError = true;
Branches = Array.Empty<GitHubBranch>();
}
}
}
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 sind in folgenden Fällen eine gute Wahl:
HttpClient
.HttpClient
s verfügen über unterschiedliche Konfigurationen.Die Konfiguration eines benannten HttpClient
kann während der Registrierung in Startup.ConfigureServices
angegeben werden:
services.AddHttpClient("github", c =>
{
c.BaseAddress = new Uri("https://api.github.com/");
// Github API versioning
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
// Github requires a user-agent
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});
Im vorangehenden Code wird der Client mit Folgendem konfiguriert:
https://api.github.com/
Jedes Mal, wenn CreateClient aufgerufen wird, geschieht Folgendes:
HttpClient
wird erstellt.Übergeben Sie für die Erstellung eines benannten Clients dessen Namen an CreateClient
:
public class NamedClientModel : PageModel
{
private readonly IHttpClientFactory _clientFactory;
public IEnumerable<GitHubPullRequest> PullRequests { get; private set; }
public bool GetPullRequestsError { get; private set; }
public bool HasPullRequests => PullRequests.Any();
public NamedClientModel(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task OnGet()
{
var request = new HttpRequestMessage(HttpMethod.Get,
"repos/dotnet/AspNetCore.Docs/pulls");
var client = _clientFactory.CreateClient("github");
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
PullRequests = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubPullRequest>>(responseStream);
}
else
{
GetPullRequestsError = true;
PullRequests = Array.Empty<GitHubPullRequest>();
}
}
}
Im vorangehenden Code muss die Anforderung keinen Hostnamen angeben. Der Code muss nur den Pfad übergeben, da die für den Client konfigurierte Basisadresse verwendet wird.
Typisierte Clients:
HttpClient
bereit. Beispielsweise kann ein einzelner typisierter Client für Folgendes verwendet werden: Ein typisierter Client akzeptiert einen HttpClient
-Parameter in seinem Konstruktor:
public class GitHubService
{
public HttpClient Client { get; }
public GitHubService(HttpClient client)
{
client.BaseAddress = new Uri("https://api.github.com/");
// GitHub API versioning
client.DefaultRequestHeaders.Add("Accept",
"application/vnd.github.v3+json");
// GitHub requires a user-agent
client.DefaultRequestHeaders.Add("User-Agent",
"HttpClientFactory-Sample");
Client = client;
}
public async Task<IEnumerable<GitHubIssue>> GetAspNetDocsIssues()
{
return await Client.GetFromJsonAsync<IEnumerable<GitHubIssue>>(
"/repos/aspnet/AspNetCore.Docs/issues?state=open&sort=created&direction=desc");
}
}
Für den Code oben gilt:
HttpClient
-Objekt wird als öffentliche Eigenschaft zur Verfügung gestellt.Es können API-spezifische Methoden erstellt werden, die die HttpClient
-Funktionalität verfügbar machen. Die GetAspNetDocsIssues
-Methode kapselt z. B. Code zum Abrufen offener Probleme.
Der folgende Code ruft AddHttpClient in Startup.ConfigureServices
auf, um eine typisierte Clientklasse zu registrieren:
services.AddHttpClient<GitHubService>();
Der typisierte Client wird mit DI als „vorübergehend“ registriert. Im vorherigen Code registriert AddHttpClient
GitHubService
als vorübergehenden Dienst. Diese Registrierung verwendet eine Factorymethode für folgende Aktionen:
HttpClient
:GitHubService
-Instanz, wobei die Instanz von HttpClient
an ihren Konstrukt übergeben wirdDer typisierte Client kann direkt eingefügt und verwendet werden:
public class TypedClientModel : PageModel
{
private readonly GitHubService _gitHubService;
public IEnumerable<GitHubIssue> LatestIssues { get; private set; }
public bool HasIssue => LatestIssues.Any();
public bool GetIssuesError { get; private set; }
public TypedClientModel(GitHubService gitHubService)
{
_gitHubService = gitHubService;
}
public async Task OnGet()
{
try
{
LatestIssues = await _gitHubService.GetAspNetDocsIssues();
}
catch(HttpRequestException)
{
GetIssuesError = true;
LatestIssues = Array.Empty<GitHubIssue>();
}
}
}
Die Konfiguration kann für einen typisierten Client während der Registrierung in Startup.ConfigureServices
angegeben werden, anstatt im Konstruktor des typisierten Clients:
services.AddHttpClient<RepoService>(c =>
{
c.BaseAddress = new Uri("https://api.github.com/");
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});
HttpClient
kann in einem typisierten Client gekapselt werden. Anstatt ihn als eine Eigenschaft zur Verfügung zu stellen, definieren Sie eine Methode, die die HttpClient
-Instanz intern aufruft:
public class RepoService
{
// _httpClient isn't exposed publicly
private readonly HttpClient _httpClient;
public RepoService(HttpClient client)
{
_httpClient = client;
}
public async Task<IEnumerable<string>> GetRepos()
{
var response = await _httpClient.GetAsync("aspnet/repos");
response.EnsureSuccessStatusCode();
using var responseStream = await response.Content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync
<IEnumerable<string>>(responseStream);
}
}
Im vorangehenden Code wird HttpClient
in einem privaten Feld gespeichert. Der Zugriff auf HttpClient
erfolgt durch die öffentliche GetRepos
-Methode.
IHttpClientFactory
kann in Verbindung mit Bibliotheken von Drittanbietern verwendet werden, z. B. Refit. Refit ist eine REST-Bibliothek für .NET. Sie konvertiert REST-APIs in Live-Schnittstellen. Eine Implementierung der Schnittstelle wird dynamisch von RestService
generiert. HttpClient
wird für die externen HTTP-Aufrufe verwendet.
Eine Schnittstelle und eine Antwort werden definiert, um die externe API und die Antwort darzustellen:
public interface IHelloClient
{
[Get("/helloworld")]
Task<Reply> GetMessageAsync();
}
public class Reply
{
public string Message { get; set; }
}
Ein typisierter Client kann hinzugefügt werden. Refit wird zum Generieren der Implementierung verwendet:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("hello", c =>
{
c.BaseAddress = new Uri("http://localhost:5000");
})
.AddTypedClient(c => Refit.RestService.For<IHelloClient>(c));
services.AddControllers();
}
Die definierte Schnittstelle kann bei Bedarf mit der von DI und Refit bereitgestellten Implementierung verwendet werden:
[ApiController]
public class ValuesController : ControllerBase
{
private readonly IHelloClient _client;
public ValuesController(IHelloClient client)
{
_client = client;
}
[HttpGet("/")]
public async Task<ActionResult<Reply>> Index()
{
return await _client.GetMessageAsync();
}
}
In den vorangehenden Beispielen verwenden alle Anforderungen das GET-HTTP-Verb. HttpClient
unterstützt ebenso andere HTTP-Verben, einschließlich der folgenden:
Eine komplette Liste der unterstützten HTTP-Verben finden Sie unter HttpMethod.
Im folgenden Beispiel wird gezeigt, wie Sie eine HTTP-POST-Anforderung stellen:
public async Task CreateItemAsync(TodoItem todoItem)
{
var todoItemJson = new StringContent(
JsonSerializer.Serialize(todoItem, _jsonSerializerOptions),
Encoding.UTF8,
"application/json");
using var httpResponse =
await _httpClient.PostAsync("/api/TodoItems", todoItemJson);
httpResponse.EnsureSuccessStatusCode();
}
Für die CreateItemAsync
-Methoden im Code oben gilt Folgendes:
TodoItem
-Parameter mithilfe von System.Text.Json
in JSON. Dabei wird eine Instanz von JsonSerializerOptions verwenden, um den Serialisierungsprozess zu konfigurieren.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 SaveItemAsync(TodoItem todoItem)
{
var todoItemJson = new StringContent(
JsonSerializer.Serialize(todoItem),
Encoding.UTF8,
"application/json");
using var httpResponse =
await _httpClient.PutAsync($"/api/TodoItems/{todoItem.Id}", todoItemJson);
httpResponse.EnsureSuccessStatusCode();
}
Der vorangehende Code ist dem POST-Beispiel sehr ähnlich. Die SaveItemAsync
-Methode ruft PutAsync anstelle von PostAsync
auf.
Das folgende Beispiel zeigt eine HTTP-DELETE-Anforderung.
public async Task DeleteItemAsync(long itemId)
{
using var httpResponse =
await _httpClient.DeleteAsync($"/api/TodoItems/{itemId}");
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.
HttpClient
enthält das Konzept, Handler zu delegieren, die für ausgehende HTTP-Anforderungen miteinander verknüpft werden können. IHttpClientFactory
:
So erstellen Sie einen delegierenden Handler:
public class ValidateHeaderHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (!request.Headers.Contains("X-API-KEY"))
{
return new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(
"You must supply an API key header called X-API-KEY")
};
}
return await base.SendAsync(request, cancellationToken);
}
}
Der vorangehende Code überprüft, ob die Anforderung einen X-API-KEY
-Header enthält. Wenn X-API-KEY
fehlt, wird BadRequest zurückgegeben.
Für eine HttpClient
-Klasse mit Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandler kann mehr als ein Handler zur Konfiguration hinzugefügt werden:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ValidateHeaderHandler>();
services.AddHttpClient("externalservice", c =>
{
// Assume this is an "external" service which requires an API KEY
c.BaseAddress = new Uri("https://localhost:5001/");
})
.AddHttpMessageHandler<ValidateHeaderHandler>();
// Remaining code deleted for brevity.
Im vorangehenden Code ist ValidateHeaderHandler
mit DI registriert. Nach der Registrierung kann AddHttpMessageHandler aufgerufen werden, was den Typ für den Handler übergibt.
Mehrere Handler können in der Reihenfolge registriert werden, in der sie ausgeführt werden sollen. Jeder Handler umschließt den nächsten Handler, bis der endgültige HttpClientHandler
die Anforderung ausführt:
services.AddTransient<SecureRequestHandler>();
services.AddTransient<RequestDataHandler>();
services.AddHttpClient("clientwithhandlers")
// This handler is on the outside and called first during the
// request, last during the response.
.AddHttpMessageHandler<SecureRequestHandler>()
// This handler is on the inside, closest to the request being
// sent.
.AddHttpMessageHandler<RequestDataHandler>();
Wenn IHttpClientFactory
einen neuen delegierenden Handler erstellt, wird DI verwendet, um die Konstruktorparameter des Handlers zu erfüllen. IHttpClientFactory
erstellt einen separaten DI-Bereich für jeden Handler. Dies kann zu überraschendem Verhalten führen, wenn ein Handler einen bereichsbezogenen Dienst nutzt.
Sehen Sie sich beispielsweise die folgende Schnittstelle und ihre Implementierung an, die eine Aufgabe als Vorgang mit einem Bezeichner OperationId
darstellt:
public interface IOperationScoped
{
string OperationId { get; }
}
public class OperationScoped : IOperationScoped
{
public string OperationId { get; } = Guid.NewGuid().ToString()[^4..];
}
Wie der Name bereits vermuten lässt, wird IOperationScoped
bei DI registriert, wobei eine bereichsbezogene Lebensdauer verwendet wird:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<TodoContext>(options =>
options.UseInMemoryDatabase("TodoItems"));
services.AddHttpContextAccessor();
services.AddHttpClient<TodoClient>((sp, httpClient) =>
{
var httpRequest = sp.GetRequiredService<IHttpContextAccessor>().HttpContext.Request;
// For sample purposes, assume TodoClient is used in the context of an incoming request.
httpClient.BaseAddress = new Uri(UriHelper.BuildAbsolute(httpRequest.Scheme,
httpRequest.Host, httpRequest.PathBase));
httpClient.Timeout = TimeSpan.FromSeconds(5);
});
services.AddScoped<IOperationScoped, OperationScoped>();
services.AddTransient<OperationHandler>();
services.AddTransient<OperationResponseHandler>();
services.AddHttpClient("Operation")
.AddHttpMessageHandler<OperationHandler>()
.AddHttpMessageHandler<OperationResponseHandler>()
.SetHandlerLifetime(TimeSpan.FromSeconds(5));
services.AddControllers();
services.AddRazorPages();
}
Der folgende delegierende Handler verwendet IOperationScoped
, um den X-OPERATION-ID
-Header für die ausgehende Anforderung festzulegen:
public class OperationHandler : DelegatingHandler
{
private readonly IOperationScoped _operationService;
public OperationHandler(IOperationScoped operationScoped)
{
_operationService = operationScoped;
}
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("X-OPERATION-ID", _operationService.OperationId);
return await base.SendAsync(request, cancellationToken);
}
}
Navigieren Sie im HttpRequestsSample
-Download] zu /Operation
, und aktualisieren Sie die Seite. Der Wert für den Anforderungsbereich ändert sich für jede Anforderung, aber der Wert des Handlerbereichs ändert sich nur alle 5 Sekunden.
Handler können von Diensten eines beliebigen Bereichs abhängen. Dienste, von denen Handler abhängig sind, werden verworfen, wenn der Handler verworfen wird.
Verwenden Sie einen der folgenden Ansätze, um den anforderungsspezifischen Zustand mit Meldungshandlern zu teilen:
IHttpClientFactory
ist mit der Drittanbieterbibliothek Polly integriert. Polly ist eine umfassende Bibliothek für die Behandlung von beständigen und vorübergehenden Fehlern für .NET. Entwicklern wird damit ermöglicht, Richtlinien wie Wiederholungsrichtlinien, Trennschalterrichtlinien, Timeout-Richtlinien, Bulkhead Isolation-Richtlinien und Ausweichrichtlinien in einer flüssigen und threadsicheren Art und Weise auszudrücken.
Erweiterungsmethoden werden bereitgestellt, um die Verwendung von Polly-Richtlinien mit konfigurierten HttpClient
-Instanzen zu aktivieren. Die Polly-Erweiterungen unterstützen das Hinzufügen von Polly-basierten Handlern zu Clients. Polly erfordert das Microsoft.Extensions.Http.Polly-NuGet-Paket.
Fehler treten normalerweise auf, wenn externe HTTP-Aufrufe vorübergehend sind. AddTransientHttpErrorPolicy ermöglicht die Definition einer Richtlinie, um vorübergehende Fehler zu behandeln. Mit AddTransientHttpErrorPolicy
konfigurierte Richtlinien behandeln die folgenden Antworten:
AddTransientHttpErrorPolicy
ermöglicht den Zugriff auf ein PolicyBuilder
-Objekt, das für die Fehlerbehandlung konfiguriert wurde und mögliche vorübergehende Fehler darstellt:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<UnreliableEndpointCallerService>()
.AddTransientHttpErrorPolicy(p =>
p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));
// Remaining code deleted for brevity.
Im vorangehenden Code wird eine WaitAndRetryAsync
-Richtlinie definiert. Anforderungsfehler werden bis zu dreimal mit einer Verzögerung von 600 ms wiederholt.
Erweiterungsmethoden werden zum Hinzufügen von Polly-basierten Handlern bereitgestellt, z. B. AddPolicyHandler. Der folgende AddPolicyHandler
-Überladung prüft die Anforderung, um zu entscheiden, welche Richtlinie angewendet werden soll:
var timeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(10));
var longTimeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(30));
services.AddHttpClient("conditionalpolicy")
// Run some code to select a policy based on the request
.AddPolicyHandler(request =>
request.Method == HttpMethod.Get ? timeout : longTimeout);
Im vorangehenden Code wird ein 10 Sekunden langer Timeout angewendet, wenn die ausgehende Anforderung eine HTTP GET-Anforderung ist. Für alle anderen HTTP-Methoden wird ein 30 Sekunden langer Timeout verwendet.
Es ist üblich, Polly-Richtlinien zu schachteln:
services.AddHttpClient("multiplepolicies")
.AddTransientHttpErrorPolicy(p => p.RetryAsync(3))
.AddTransientHttpErrorPolicy(
p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
Im vorherigen Beispiel:
AddTransientHttpErrorPolicy
-Aufruf fügt eine Trennschalterrichtlinie hinzu. Weitere externe Anforderungen werden 30 Sekunden lang blockiert, wenn fünf Fehlversuche hintereinander stattfinden. Trennschalterrichtlinien sind zustandsbehaftet. Alle Aufrufe über diesen Client teilen den gleichen Schalterzustand.Eine Methode zum Verwalten regelmäßig genutzter Richtlinien ist, Sie einmal zu definieren und mit PolicyRegistry
zu registrieren.
Im folgenden Code wird Folgendes ausgeführt:
public void ConfigureServices(IServiceCollection services)
{
var timeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(10));
var longTimeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(30));
var registry = services.AddPolicyRegistry();
registry.Add("regular", timeout);
registry.Add("long", longTimeout);
services.AddHttpClient("regularTimeoutHandler")
.AddPolicyHandlerFromRegistry("regular");
services.AddHttpClient("longTimeoutHandler")
.AddPolicyHandlerFromRegistry("long");
// Remaining code deleted for brevity.
Weitere Informationen zu IHttpClientFactory
und Polly-Integrationen finden Sie im Polly-Wiki.
Bei jedem Aufruf von CreateClient
in der IHttpClientFactory
wird eine neue Instanz von HttpClient
zurückgegeben. Pro benannter Client wird ein HttpMessageHandler erstellt. Die Factory verwaltet die Lebensdauer der HttpMessageHandler
-Instanzen.
IHttpClientFactory
legt die HttpMessageHandler
-Instanzen zusammen, die von der Factory zum Reduzieren des Ressourcenverbrauchs erstellt wurden. Eine HttpMessageHandler
-Instanz kann aus dem Pool wiederverwendet werden, wenn eine neue HttpClient
-Instanz erstellt wird und deren Lebensdauer noch nicht abgelaufen ist.
Das Zusammenlegen von Handlern ist ein wünschenswerter Vorgang, da jeder Handler in der Regel seine zugrunde liegenden HTTP-Verbindungen selbst verwaltet. Wenn mehr Handler als nötig erstellt werden, können Verzögerungen bei Verbindungen entstehen. Einige Handler halten Verbindungen auch unbegrenzt offen, was verhindert, dass der Handler auf DNS-Änderungen (Domain Name System) reagiert.
Die Standardlebensdauer von Handlern beträgt zwei Minuten. Der Standardwert kann für jeden benannten Client überschrieben werden:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("extendedhandlerlifetime")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
// Remaining code deleted for brevity.
HttpClient
-Instanzen können im Allgemeinen als .NET-Objekte behandelt werden, die nicht verworfen werden müssen. Beim Verwerfen werden ausgehende Anforderungen abgebrochen, und es wird sichergestellt, dass die angegebene HttpClient
-Instanz nach dem Aufruf von Dispose nicht mehr verwendet werden kann. IHttpClientFactory
verfolgt von HttpClient
-Instanzen verwendete Ressourcen nach und verwirft sie.
Das Beibehalten einer einzelnen HttpClient
-Instanz für einen langen Zeitraum ist ein allgemeines Muster, das vor der Einführung von IHttpClientFactory
verwendet wurde. Dieses Muster wird nach der Migration zu IHttpClientFactory
überflüssig.
Mit der Verwendung von IHttpClientFactory
in einer DI-fähigen App wird Folgendes vermieden:
HttpMessageHandler
-InstanzenHttpMessageHandler
-InstanzenEs gibt alternative Möglichkeiten zum Lösen des vorangehenden Problems mithilfe einer langlebigen SocketsHttpHandler-Instanz:
SocketsHttpHandler
-Instanz, wenn die App gestartet wird, und verwenden Sie diese für die Lebensdauer der App.HttpClient
-Instanzen mithilfe von new HttpClient(handler, disposeHandler: false)
.Diese Ansätze lösen die Ressourcenverwaltungsprobleme, die IHttpClientFactory
auf ähnliche Weise löst.
SocketsHttpHandler
gibt Verbindungen für HttpClient
-Instanzen frei. Durch diese Freigabe wird die Erschöpfung von Sockets vermieden.SocketsHttpHandler
wechselt die Verbindungen anhand von PooledConnectionLifetime
, um Probleme durch veraltetes DNS zu umgehen.Die zusammengelegten HttpMessageHandler
-Instanzen resultieren in der Freigabe von CookieContainer
-Objekten. Die unerwartete Freigabe von CookieContainer
-Objekten führt oft zu fehlerhaftem Code. Ziehen Sie für Apps, die Cookies erfordern, folgende Vorgehensweisen in Betracht:
IHttpClientFactory
Rufen Sie ConfigurePrimaryHttpMessageHandler auf, um die automatische cookieverarbeitung zu deaktivieren:
services.AddHttpClient("configured-disable-automatic-cookies")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
UseCookies = false,
};
});
Über IHttpClientFactory
erstellte Clients zeichnen Protokollmeldungen für alle Anforderungen auf. Aktivieren Sie die entsprechende Informationsebene in der Protokollierungskonfiguration, um die Standardprotokollmeldungen anzuzeigen. Zusätzliche Protokollierung, z.B. das Protokollieren von Anforderungsheadern, wird nur auf der Ablaufverfolgungsebene enthalten.
Die Protokollierungskategorie für jeden Client enthält den Namen des Clients. Ein Client namens MyNamedClient protokolliert z. B. Nachrichten mit der Kategorie „System.Net.Http.HttpClient.MyNamedClient.LogicalHandler“. Meldungen mit dem Suffix LogicalHandler treten außerhalb der Anforderungshandlerpipeline auf. In der Anforderung werden Meldungen protokolliert, bevor andere Handler in der Pipeline sie verarbeitet haben. In der Antwort werden Meldungen protokolliert, nachdem andere Handler in der Pipeline die Antwort empfangen haben.
Die Protokollierung tritt ebenfalls innerhalb der Anforderungshandlerpipeline auf. Im Beispiel von MyNamedClient werden diese Nachrichten mit der Protokollkategorie „System.Net.Http.HttpClient.MyNamedClient.ClientHandler“ protokolliert. Für die Anforderung erfolgt dies, nachdem alle anderen Handler ausgeführt wurden und bevor die Anforderung gesendet wird. Diese Protokollierung enthält den Zustand der Antwort in der Antwort, bevor sie an die Handlerpipeline zurückgegeben wird.
Das Aktivieren der Protokollierung außerhalb und innerhalb der Pipeline ermöglicht die Überprüfung der Änderungen, die durch andere Handler in der Pipeline erfolgt sind. Dies kann die Änderungen an Anforderungsheadern oder am Statuscode der Antwort enthalten.
Das Einschließen des Namens des Clients in der Protokollkategorie aktiviert die Protokollfilterung für spezifische benannte Clients.
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 verwendet werden. Der Delegat wird verwendet, um den primären HttpMessageHandler
zu erstellen und konfigurieren, der von dem Client verwendet wird:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("configured-inner-handler")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
AllowAutoRedirect = false,
UseDefaultCredentials = true
};
});
// Remaining code deleted for brevity.
Fügen Sie in einer Konsolen-App dem Projekt die folgenden Paketverweise hinzu:
Im folgenden Beispiel:
MyService
erstellt eine Clientfactoryinstanz aus dem-Dienst, der zum Erstellen eines HttpClient
verwendet wird. HttpClient
wird zum Abrufen einer Webseite verwendet.Main
erstellt einen Bereich, um die GetPage
-Methode des Diensts auszuführen und die ersten 500 Zeichen des Webseiteninhalts in die Konsole zu schreiben.using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
class Program
{
static async Task<int> Main(string[] args)
{
var builder = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddHttpClient();
services.AddTransient<IMyService, MyService>();
}).UseConsoleLifetime();
var host = builder.Build();
try
{
var myService = host.Services.GetRequiredService<IMyService>();
var pageContent = await myService.GetPage();
Console.WriteLine(pageContent.Substring(0, 500));
}
catch (Exception ex)
{
var logger = host.Services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred.");
}
return 0;
}
public interface IMyService
{
Task<string> GetPage();
}
public class MyService : IMyService
{
private readonly IHttpClientFactory _clientFactory;
public MyService(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task<string> GetPage()
{
// Content from BBC One: Dr. Who website (©BBC)
var request = new HttpRequestMessage(HttpMethod.Get,
"https://www.bbc.co.uk/programmes/b006q2x0");
var client = _clientFactory.CreateClient();
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
else
{
return $"StatusCode: {response.StatusCode}";
}
}
}
}
Für die Headerweitergabe wird eine ASP.NET Core-Middleware verwendet, um HTTP-Header von eingehenden Anforderungen an ausgehende HTTP-Clientanforderungen weiterzugeben. So verwenden Sie die Headerweitergabe:
Erstellen Sie einen Verweis auf das Microsoft.AspNetCore.HeaderPropagation-Paket.
Konfigurieren Sie in Startup
die Middleware und HttpClient
:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddHttpClient("MyForwardingClient").AddHeaderPropagation();
services.AddHeaderPropagation(options =>
{
options.Headers.Add("X-TraceId");
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseHeaderPropagation();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Der Client schließt die konfigurierten Header für ausgehende Anforderungen ein:
var client = clientFactory.CreateClient("MyForwardingClient");
var response = client.GetAsync(...);
Von Kirk Larkin, Steve Gordon, Glenn Condron und Ryan Nowak.
IHttpClientFactory kann registriert und zum Konfigurieren und Erstellen von HttpClient-Instanzen in einer App verwendet werden. IHttpClientFactory
bietet die folgenden Vorteile:
HttpClient
-Instanzen wird damit geboten. Beispielsweise kann ein Client namens github registriert und für den Zugriff auf GitHub konfiguriert werden. Ein Standardclient kann für den allgemeinen Zugriff registriert werden.HttpClient
in Code umgesetzt. Außerdem werden Erweiterungen für auf Polly basierende Middleware bereitgestellt, um die delegierenden Handler in HttpClient
zu nutzen.HttpClientMessageHandler
-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.ILogger
) für alle Anforderungen hinzugefügt, die über Clients gesendet werden, die von der Factory erstellt wurden.Zeigen Sie Beispielcode an, oder laden Sie diesen herunter (Vorgehensweise zum Herunterladen).
Der Beispielcode in dieser Version des Themas verwendet System.Text.Json, um JSON-Inhalte zu deserialisieren, die in HTTP-Antworten zurückgegeben wurden. Verwenden Sie bei Beispielen, die Json.NET
und ReadAsAsync<T>
verwenden, die Versionsauswahl, um eine 2.x-Version dieses Themas auszuwählen.
Es gibt mehrere Möglichkeiten IHttpClientFactory
in einer App zu verwenden:
Der beste Ansatz richtet sich nach den Anforderungen der App.
IHttpClientFactory
kann durch Aufrufen von AddHttpClient
registriert werden:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
// Remaining code deleted for brevity.
Eine IHttpClientFactory
-Schnittstelle kann mithilfe der Dependency Injection (DI) angefordert werden. Im folgenden Code wird IHttpClientFactory
verwendet, um eine HttpClient
-Instanz zu erstellen:
public class BasicUsageModel : PageModel
{
private readonly IHttpClientFactory _clientFactory;
public IEnumerable<GitHubBranch> Branches { get; private set; }
public bool GetBranchesError { get; private set; }
public BasicUsageModel(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task OnGet()
{
var request = new HttpRequestMessage(HttpMethod.Get,
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches");
request.Headers.Add("Accept", "application/vnd.github.v3+json");
request.Headers.Add("User-Agent", "HttpClientFactory-Sample");
var client = _clientFactory.CreateClient();
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
Branches = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(responseStream);
}
else
{
GetBranchesError = true;
Branches = Array.Empty<GitHubBranch>();
}
}
}
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 sind in folgenden Fällen eine gute Wahl:
HttpClient
.HttpClient
s verfügen über unterschiedliche Konfigurationen.Die Konfiguration eines benannten HttpClient
kann während der Registrierung in Startup.ConfigureServices
angegeben werden:
services.AddHttpClient("github", c =>
{
c.BaseAddress = new Uri("https://api.github.com/");
// Github API versioning
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
// Github requires a user-agent
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});
Im vorangehenden Code wird der Client mit Folgendem konfiguriert:
https://api.github.com/
Jedes Mal, wenn CreateClient aufgerufen wird, geschieht Folgendes:
HttpClient
wird erstellt.Übergeben Sie für die Erstellung eines benannten Clients dessen Namen an CreateClient
:
public class NamedClientModel : PageModel
{
private readonly IHttpClientFactory _clientFactory;
public IEnumerable<GitHubPullRequest> PullRequests { get; private set; }
public bool GetPullRequestsError { get; private set; }
public bool HasPullRequests => PullRequests.Any();
public NamedClientModel(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task OnGet()
{
var request = new HttpRequestMessage(HttpMethod.Get,
"repos/dotnet/AspNetCore.Docs/pulls");
var client = _clientFactory.CreateClient("github");
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
PullRequests = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubPullRequest>>(responseStream);
}
else
{
GetPullRequestsError = true;
PullRequests = Array.Empty<GitHubPullRequest>();
}
}
}
Im vorangehenden Code muss die Anforderung keinen Hostnamen angeben. Der Code muss nur den Pfad übergeben, da die für den Client konfigurierte Basisadresse verwendet wird.
Typisierte Clients:
HttpClient
bereit. Beispielsweise kann ein einzelner typisierter Client für Folgendes verwendet werden: Ein typisierter Client akzeptiert einen HttpClient
-Parameter in seinem Konstruktor:
public class GitHubService
{
public HttpClient Client { get; }
public GitHubService(HttpClient client)
{
client.BaseAddress = new Uri("https://api.github.com/");
// GitHub API versioning
client.DefaultRequestHeaders.Add("Accept",
"application/vnd.github.v3+json");
// GitHub requires a user-agent
client.DefaultRequestHeaders.Add("User-Agent",
"HttpClientFactory-Sample");
Client = client;
}
public async Task<IEnumerable<GitHubIssue>> GetAspNetDocsIssues()
{
var response = await Client.GetAsync(
"/repos/dotnet/AspNetCore.Docs/issues?state=open&sort=created&direction=desc");
response.EnsureSuccessStatusCode();
using var responseStream = await response.Content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubIssue>>(responseStream);
}
}
Wenn Sie möchten, dass Codekommentare in anderen Sprachen als Englisch angezeigt werden, informieren Sie uns in diesem GitHub-Issue.
Für den Code oben gilt:
HttpClient
-Objekt wird als öffentliche Eigenschaft zur Verfügung gestellt.Es können API-spezifische Methoden erstellt werden, die die HttpClient
-Funktionalität verfügbar machen. Die GetAspNetDocsIssues
-Methode kapselt z. B. Code zum Abrufen offener Probleme.
Der folgende Code ruft AddHttpClient in Startup.ConfigureServices
auf, um eine typisierte Clientklasse zu registrieren:
services.AddHttpClient<GitHubService>();
Der typisierte Client wird mit DI als „vorübergehend“ registriert. Im vorherigen Code registriert AddHttpClient
GitHubService
als vorübergehenden Dienst. Diese Registrierung verwendet eine Factorymethode für folgende Aktionen:
HttpClient
:GitHubService
-Instanz, wobei die Instanz von HttpClient
an ihren Konstrukt übergeben wirdDer typisierte Client kann direkt eingefügt und verwendet werden:
public class TypedClientModel : PageModel
{
private readonly GitHubService _gitHubService;
public IEnumerable<GitHubIssue> LatestIssues { get; private set; }
public bool HasIssue => LatestIssues.Any();
public bool GetIssuesError { get; private set; }
public TypedClientModel(GitHubService gitHubService)
{
_gitHubService = gitHubService;
}
public async Task OnGet()
{
try
{
LatestIssues = await _gitHubService.GetAspNetDocsIssues();
}
catch(HttpRequestException)
{
GetIssuesError = true;
LatestIssues = Array.Empty<GitHubIssue>();
}
}
}
Die Konfiguration kann für einen typisierten Client während der Registrierung in Startup.ConfigureServices
angegeben werden, anstatt im Konstruktor des typisierten Clients:
services.AddHttpClient<RepoService>(c =>
{
c.BaseAddress = new Uri("https://api.github.com/");
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});
HttpClient
kann in einem typisierten Client gekapselt werden. Anstatt ihn als eine Eigenschaft zur Verfügung zu stellen, definieren Sie eine Methode, die die HttpClient
-Instanz intern aufruft:
public class RepoService
{
// _httpClient isn't exposed publicly
private readonly HttpClient _httpClient;
public RepoService(HttpClient client)
{
_httpClient = client;
}
public async Task<IEnumerable<string>> GetRepos()
{
var response = await _httpClient.GetAsync("aspnet/repos");
response.EnsureSuccessStatusCode();
using var responseStream = await response.Content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync
<IEnumerable<string>>(responseStream);
}
}
Im vorangehenden Code wird HttpClient
in einem privaten Feld gespeichert. Der Zugriff auf HttpClient
erfolgt durch die öffentliche GetRepos
-Methode.
IHttpClientFactory
kann in Verbindung mit Bibliotheken von Drittanbietern verwendet werden, z. B. Refit. Refit ist eine REST-Bibliothek für .NET. Sie konvertiert REST-APIs in Live-Schnittstellen. Eine Implementierung der Schnittstelle wird dynamisch von RestService
generiert. HttpClient
wird für die externen HTTP-Aufrufe verwendet.
Eine Schnittstelle und eine Antwort werden definiert, um die externe API und die Antwort darzustellen:
public interface IHelloClient
{
[Get("/helloworld")]
Task<Reply> GetMessageAsync();
}
public class Reply
{
public string Message { get; set; }
}
Ein typisierter Client kann hinzugefügt werden. Refit wird zum Generieren der Implementierung verwendet:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("hello", c =>
{
c.BaseAddress = new Uri("http://localhost:5000");
})
.AddTypedClient(c => Refit.RestService.For<IHelloClient>(c));
services.AddControllers();
}
Die definierte Schnittstelle kann bei Bedarf mit der von DI und Refit bereitgestellten Implementierung verwendet werden:
[ApiController]
public class ValuesController : ControllerBase
{
private readonly IHelloClient _client;
public ValuesController(IHelloClient client)
{
_client = client;
}
[HttpGet("/")]
public async Task<ActionResult<Reply>> Index()
{
return await _client.GetMessageAsync();
}
}
In den vorangehenden Beispielen verwenden alle Anforderungen das GET-HTTP-Verb. HttpClient
unterstützt ebenso andere HTTP-Verben, einschließlich der folgenden:
Eine komplette Liste der unterstützten HTTP-Verben finden Sie unter HttpMethod.
Im folgenden Beispiel wird gezeigt, wie Sie eine HTTP-POST-Anforderung stellen:
public async Task CreateItemAsync(TodoItem todoItem)
{
var todoItemJson = new StringContent(
JsonSerializer.Serialize(todoItem, _jsonSerializerOptions),
Encoding.UTF8,
"application/json");
using var httpResponse =
await _httpClient.PostAsync("/api/TodoItems", todoItemJson);
httpResponse.EnsureSuccessStatusCode();
}
Für die CreateItemAsync
-Methoden im Code oben gilt Folgendes:
TodoItem
-Parameter mithilfe von System.Text.Json
in JSON. Dabei wird eine Instanz von JsonSerializerOptions verwenden, um den Serialisierungsprozess zu konfigurieren.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 SaveItemAsync(TodoItem todoItem)
{
var todoItemJson = new StringContent(
JsonSerializer.Serialize(todoItem),
Encoding.UTF8,
"application/json");
using var httpResponse =
await _httpClient.PutAsync($"/api/TodoItems/{todoItem.Id}", todoItemJson);
httpResponse.EnsureSuccessStatusCode();
}
Der vorangehende Code ist dem POST-Beispiel sehr ähnlich. Die SaveItemAsync
-Methode ruft PutAsync anstelle von PostAsync
auf.
Das folgende Beispiel zeigt eine HTTP-DELETE-Anforderung.
public async Task DeleteItemAsync(long itemId)
{
using var httpResponse =
await _httpClient.DeleteAsync($"/api/TodoItems/{itemId}");
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.
HttpClient
enthält das Konzept, Handler zu delegieren, die für ausgehende HTTP-Anforderungen miteinander verknüpft werden können. IHttpClientFactory
:
So erstellen Sie einen delegierenden Handler:
public class ValidateHeaderHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (!request.Headers.Contains("X-API-KEY"))
{
return new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(
"You must supply an API key header called X-API-KEY")
};
}
return await base.SendAsync(request, cancellationToken);
}
}
Der vorangehende Code überprüft, ob die Anforderung einen X-API-KEY
-Header enthält. Wenn X-API-KEY
fehlt, wird BadRequest zurückgegeben.
Für eine HttpClient
-Klasse mit Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandler kann mehr als ein Handler zur Konfiguration hinzugefügt werden:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ValidateHeaderHandler>();
services.AddHttpClient("externalservice", c =>
{
// Assume this is an "external" service which requires an API KEY
c.BaseAddress = new Uri("https://localhost:5001/");
})
.AddHttpMessageHandler<ValidateHeaderHandler>();
// Remaining code deleted for brevity.
Im vorangehenden Code ist ValidateHeaderHandler
mit DI registriert. Nach der Registrierung kann AddHttpMessageHandler aufgerufen werden, was den Typ für den Handler übergibt.
Mehrere Handler können in der Reihenfolge registriert werden, in der sie ausgeführt werden sollen. Jeder Handler umschließt den nächsten Handler, bis der endgültige HttpClientHandler
die Anforderung ausführt:
services.AddTransient<SecureRequestHandler>();
services.AddTransient<RequestDataHandler>();
services.AddHttpClient("clientwithhandlers")
// This handler is on the outside and called first during the
// request, last during the response.
.AddHttpMessageHandler<SecureRequestHandler>()
// This handler is on the inside, closest to the request being
// sent.
.AddHttpMessageHandler<RequestDataHandler>();
Wenn IHttpClientFactory
einen neuen delegierenden Handler erstellt, wird DI verwendet, um die Konstruktorparameter des Handlers zu erfüllen. IHttpClientFactory
erstellt einen separaten DI-Bereich für jeden Handler. Dies kann zu überraschendem Verhalten führen, wenn ein Handler einen bereichsbezogenen Dienst nutzt.
Sehen Sie sich beispielsweise die folgende Schnittstelle und ihre Implementierung an, die eine Aufgabe als Vorgang mit einem Bezeichner OperationId
darstellt:
public interface IOperationScoped
{
string OperationId { get; }
}
public class OperationScoped : IOperationScoped
{
public string OperationId { get; } = Guid.NewGuid().ToString()[^4..];
}
Wie der Name bereits vermuten lässt, wird IOperationScoped
bei DI registriert, wobei eine bereichsbezogene Lebensdauer verwendet wird:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<TodoContext>(options =>
options.UseInMemoryDatabase("TodoItems"));
services.AddHttpContextAccessor();
services.AddHttpClient<TodoClient>((sp, httpClient) =>
{
var httpRequest = sp.GetRequiredService<IHttpContextAccessor>().HttpContext.Request;
// For sample purposes, assume TodoClient is used in the context of an incoming request.
httpClient.BaseAddress = new Uri(UriHelper.BuildAbsolute(httpRequest.Scheme,
httpRequest.Host, httpRequest.PathBase));
httpClient.Timeout = TimeSpan.FromSeconds(5);
});
services.AddScoped<IOperationScoped, OperationScoped>();
services.AddTransient<OperationHandler>();
services.AddTransient<OperationResponseHandler>();
services.AddHttpClient("Operation")
.AddHttpMessageHandler<OperationHandler>()
.AddHttpMessageHandler<OperationResponseHandler>()
.SetHandlerLifetime(TimeSpan.FromSeconds(5));
services.AddControllers();
services.AddRazorPages();
}
Der folgende delegierende Handler verwendet IOperationScoped
, um den X-OPERATION-ID
-Header für die ausgehende Anforderung festzulegen:
public class OperationHandler : DelegatingHandler
{
private readonly IOperationScoped _operationService;
public OperationHandler(IOperationScoped operationScoped)
{
_operationService = operationScoped;
}
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("X-OPERATION-ID", _operationService.OperationId);
return await base.SendAsync(request, cancellationToken);
}
}
Navigieren Sie im HttpRequestsSample
-Download] zu /Operation
, und aktualisieren Sie die Seite. Der Wert für den Anforderungsbereich ändert sich für jede Anforderung, aber der Wert des Handlerbereichs ändert sich nur alle 5 Sekunden.
Handler können von Diensten eines beliebigen Bereichs abhängen. Dienste, von denen Handler abhängig sind, werden verworfen, wenn der Handler verworfen wird.
Verwenden Sie einen der folgenden Ansätze, um den anforderungsspezifischen Zustand mit Meldungshandlern zu teilen:
IHttpClientFactory
ist mit der Drittanbieterbibliothek Polly integriert. Polly ist eine umfassende Bibliothek für die Behandlung von beständigen und vorübergehenden Fehlern für .NET. Entwicklern wird damit ermöglicht, Richtlinien wie Wiederholungsrichtlinien, Trennschalterrichtlinien, Timeout-Richtlinien, Bulkhead Isolation-Richtlinien und Ausweichrichtlinien in einer flüssigen und threadsicheren Art und Weise auszudrücken.
Erweiterungsmethoden werden bereitgestellt, um die Verwendung von Polly-Richtlinien mit konfigurierten HttpClient
-Instanzen zu aktivieren. Die Polly-Erweiterungen unterstützen das Hinzufügen von Polly-basierten Handlern zu Clients. Polly erfordert das Microsoft.Extensions.Http.Polly-NuGet-Paket.
Fehler treten normalerweise auf, wenn externe HTTP-Aufrufe vorübergehend sind. AddTransientHttpErrorPolicy ermöglicht die Definition einer Richtlinie, um vorübergehende Fehler zu behandeln. Mit AddTransientHttpErrorPolicy
konfigurierte Richtlinien behandeln die folgenden Antworten:
AddTransientHttpErrorPolicy
ermöglicht den Zugriff auf ein PolicyBuilder
-Objekt, das für die Fehlerbehandlung konfiguriert wurde und mögliche vorübergehende Fehler darstellt:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<UnreliableEndpointCallerService>()
.AddTransientHttpErrorPolicy(p =>
p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));
// Remaining code deleted for brevity.
Im vorangehenden Code wird eine WaitAndRetryAsync
-Richtlinie definiert. Anforderungsfehler werden bis zu dreimal mit einer Verzögerung von 600 ms wiederholt.
Erweiterungsmethoden werden zum Hinzufügen von Polly-basierten Handlern bereitgestellt, z. B. AddPolicyHandler. Der folgende AddPolicyHandler
-Überladung prüft die Anforderung, um zu entscheiden, welche Richtlinie angewendet werden soll:
var timeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(10));
var longTimeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(30));
services.AddHttpClient("conditionalpolicy")
// Run some code to select a policy based on the request
.AddPolicyHandler(request =>
request.Method == HttpMethod.Get ? timeout : longTimeout);
Im vorangehenden Code wird ein 10 Sekunden langer Timeout angewendet, wenn die ausgehende Anforderung eine HTTP GET-Anforderung ist. Für alle anderen HTTP-Methoden wird ein 30 Sekunden langer Timeout verwendet.
Es ist üblich, Polly-Richtlinien zu schachteln:
services.AddHttpClient("multiplepolicies")
.AddTransientHttpErrorPolicy(p => p.RetryAsync(3))
.AddTransientHttpErrorPolicy(
p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
Im vorherigen Beispiel:
AddTransientHttpErrorPolicy
-Aufruf fügt eine Trennschalterrichtlinie hinzu. Weitere externe Anforderungen werden 30 Sekunden lang blockiert, wenn fünf Fehlversuche hintereinander stattfinden. Trennschalterrichtlinien sind zustandsbehaftet. Alle Aufrufe über diesen Client teilen den gleichen Schalterzustand.Eine Methode zum Verwalten regelmäßig genutzter Richtlinien ist, Sie einmal zu definieren und mit PolicyRegistry
zu registrieren.
Im folgenden Code wird Folgendes ausgeführt:
public void ConfigureServices(IServiceCollection services)
{
var timeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(10));
var longTimeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(30));
var registry = services.AddPolicyRegistry();
registry.Add("regular", timeout);
registry.Add("long", longTimeout);
services.AddHttpClient("regularTimeoutHandler")
.AddPolicyHandlerFromRegistry("regular");
services.AddHttpClient("longTimeoutHandler")
.AddPolicyHandlerFromRegistry("long");
// Remaining code deleted for brevity.
Weitere Informationen zu IHttpClientFactory
und Polly-Integrationen finden Sie im Polly-Wiki.
Bei jedem Aufruf von CreateClient
in der IHttpClientFactory
wird eine neue Instanz von HttpClient
zurückgegeben. Pro benannter Client wird ein HttpMessageHandler erstellt. Die Factory verwaltet die Lebensdauer der HttpMessageHandler
-Instanzen.
IHttpClientFactory
legt die HttpMessageHandler
-Instanzen zusammen, die von der Factory zum Reduzieren des Ressourcenverbrauchs erstellt wurden. Eine HttpMessageHandler
-Instanz kann aus dem Pool wiederverwendet werden, wenn eine neue HttpClient
-Instanz erstellt wird und deren Lebensdauer noch nicht abgelaufen ist.
Das Zusammenlegen von Handlern ist ein wünschenswerter Vorgang, da jeder Handler in der Regel seine zugrunde liegenden HTTP-Verbindungen selbst verwaltet. Wenn mehr Handler als nötig erstellt werden, können Verzögerungen bei Verbindungen entstehen. Einige Handler halten Verbindungen auch unbegrenzt offen, was verhindert, dass der Handler auf DNS-Änderungen (Domain Name System) reagiert.
Die Standardlebensdauer von Handlern beträgt zwei Minuten. Der Standardwert kann für jeden benannten Client überschrieben werden:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("extendedhandlerlifetime")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
// Remaining code deleted for brevity.
HttpClient
-Instanzen können im Allgemeinen als .NET-Objekte behandelt werden, die nicht verworfen werden müssen. Beim Verwerfen werden ausgehende Anforderungen abgebrochen, und es wird sichergestellt, dass die angegebene HttpClient
-Instanz nach dem Aufruf von Dispose nicht mehr verwendet werden kann. IHttpClientFactory
verfolgt von HttpClient
-Instanzen verwendete Ressourcen nach und verwirft sie.
Das Beibehalten einer einzelnen HttpClient
-Instanz für einen langen Zeitraum ist ein allgemeines Muster, das vor der Einführung von IHttpClientFactory
verwendet wurde. Dieses Muster wird nach der Migration zu IHttpClientFactory
überflüssig.
Mit der Verwendung von IHttpClientFactory
in einer DI-fähigen App wird Folgendes vermieden:
HttpMessageHandler
-InstanzenHttpMessageHandler
-InstanzenEs gibt alternative Möglichkeiten zum Lösen des vorangehenden Problems mithilfe einer langlebigen SocketsHttpHandler-Instanz:
SocketsHttpHandler
-Instanz, wenn die App gestartet wird, und verwenden Sie diese für die Lebensdauer der App.HttpClient
-Instanzen mithilfe von new HttpClient(handler, disposeHandler: false)
.Diese Ansätze lösen die Ressourcenverwaltungsprobleme, die IHttpClientFactory
auf ähnliche Weise löst.
SocketsHttpHandler
gibt Verbindungen für HttpClient
-Instanzen frei. Durch diese Freigabe wird die Erschöpfung von Sockets vermieden.SocketsHttpHandler
wechselt die Verbindungen anhand von PooledConnectionLifetime
, um Probleme durch veraltetes DNS zu umgehen.Die zusammengelegten HttpMessageHandler
-Instanzen resultieren in der Freigabe von CookieContainer
-Objekten. Die unerwartete Freigabe von CookieContainer
-Objekten führt oft zu fehlerhaftem Code. Ziehen Sie für Apps, die Cookies erfordern, folgende Vorgehensweisen in Betracht:
IHttpClientFactory
Rufen Sie ConfigurePrimaryHttpMessageHandler auf, um die automatische cookieverarbeitung zu deaktivieren:
services.AddHttpClient("configured-disable-automatic-cookies")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
UseCookies = false,
};
});
Über IHttpClientFactory
erstellte Clients zeichnen Protokollmeldungen für alle Anforderungen auf. Aktivieren Sie die entsprechende Informationsebene in der Protokollierungskonfiguration, um die Standardprotokollmeldungen anzuzeigen. Zusätzliche Protokollierung, z.B. das Protokollieren von Anforderungsheadern, wird nur auf der Ablaufverfolgungsebene enthalten.
Die Protokollierungskategorie für jeden Client enthält den Namen des Clients. Ein Client namens MyNamedClient protokolliert z. B. Nachrichten mit der Kategorie „System.Net.Http.HttpClient.MyNamedClient.LogicalHandler“. Meldungen mit dem Suffix LogicalHandler treten außerhalb der Anforderungshandlerpipeline auf. In der Anforderung werden Meldungen protokolliert, bevor andere Handler in der Pipeline sie verarbeitet haben. In der Antwort werden Meldungen protokolliert, nachdem andere Handler in der Pipeline die Antwort empfangen haben.
Die Protokollierung tritt ebenfalls innerhalb der Anforderungshandlerpipeline auf. Im Beispiel von MyNamedClient werden diese Nachrichten mit der Protokollkategorie „System.Net.Http.HttpClient.MyNamedClient.ClientHandler“ protokolliert. Für die Anforderung erfolgt dies, nachdem alle anderen Handler ausgeführt wurden und bevor die Anforderung gesendet wird. Diese Protokollierung enthält den Zustand der Antwort in der Antwort, bevor sie an die Handlerpipeline zurückgegeben wird.
Das Aktivieren der Protokollierung außerhalb und innerhalb der Pipeline ermöglicht die Überprüfung der Änderungen, die durch andere Handler in der Pipeline erfolgt sind. Dies kann die Änderungen an Anforderungsheadern oder am Statuscode der Antwort enthalten.
Das Einschließen des Namens des Clients in der Protokollkategorie aktiviert die Protokollfilterung für spezifische benannte Clients.
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 verwendet werden. Der Delegat wird verwendet, um den primären HttpMessageHandler
zu erstellen und konfigurieren, der von dem Client verwendet wird:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("configured-inner-handler")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
AllowAutoRedirect = false,
UseDefaultCredentials = true
};
});
// Remaining code deleted for brevity.
Fügen Sie in einer Konsolen-App dem Projekt die folgenden Paketverweise hinzu:
Im folgenden Beispiel:
MyService
erstellt eine Clientfactoryinstanz aus dem-Dienst, der zum Erstellen eines HttpClient
verwendet wird. HttpClient
wird zum Abrufen einer Webseite verwendet.Main
erstellt einen Bereich, um die GetPage
-Methode des Diensts auszuführen und die ersten 500 Zeichen des Webseiteninhalts in die Konsole zu schreiben.using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
class Program
{
static async Task<int> Main(string[] args)
{
var builder = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddHttpClient();
services.AddTransient<IMyService, MyService>();
}).UseConsoleLifetime();
var host = builder.Build();
try
{
var myService = host.Services.GetRequiredService<IMyService>();
var pageContent = await myService.GetPage();
Console.WriteLine(pageContent.Substring(0, 500));
}
catch (Exception ex)
{
var logger = host.Services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred.");
}
return 0;
}
public interface IMyService
{
Task<string> GetPage();
}
public class MyService : IMyService
{
private readonly IHttpClientFactory _clientFactory;
public MyService(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task<string> GetPage()
{
// Content from BBC One: Dr. Who website (©BBC)
var request = new HttpRequestMessage(HttpMethod.Get,
"https://www.bbc.co.uk/programmes/b006q2x0");
var client = _clientFactory.CreateClient();
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
else
{
return $"StatusCode: {response.StatusCode}";
}
}
}
}
Für die Headerweitergabe wird eine ASP.NET Core-Middleware verwendet, um HTTP-Header von eingehenden Anforderungen an ausgehende HTTP-Clientanforderungen weiterzugeben. So verwenden Sie die Headerweitergabe:
Erstellen Sie einen Verweis auf das Microsoft.AspNetCore.HeaderPropagation-Paket.
Konfigurieren Sie in Startup
die Middleware und HttpClient
:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddHttpClient("MyForwardingClient").AddHeaderPropagation();
services.AddHeaderPropagation(options =>
{
options.Headers.Add("X-TraceId");
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseHeaderPropagation();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Der Client schließt die konfigurierten Header für ausgehende Anforderungen ein:
var client = clientFactory.CreateClient("MyForwardingClient");
var response = client.GetAsync(...);
Von Glenn Condron, Ryan Nowak, und Steve Gordon
IHttpClientFactory kann registriert und zum Konfigurieren und Erstellen von HttpClient-Instanzen in einer App verwendet werden. Dies hat folgende Vorteile:
HttpClient
-Instanzen wird damit geboten. Ein GitHub-Client kann beispielsweise registriert werden und so konfiguriert werden, um auf GitHub zuzugreifen. Ein Standard-Client kann für andere Zwecke registriert werden.HttpClient
in Code umgesetzt. Außerdem werden Erweiterungen für auf Polly basierende Middleware bereitgestellt, die dies nutzen.HttpClientMessageHandler
-Instanzen werden verwaltet, um gängige DNS-Probleme zu vermeiden, die bei der manuellen Verwaltung der HttpClient
-Lebensdauer auftreten.ILogger
) für alle Anforderungen hinzugefügt, die über Clients gesendet werden, die von der Factory erstellt wurden.Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)
Für Projekte mit der Zielplattform .NET Framework muss das NuGet-Paket Microsoft.Extensions.Http installiert werden. Projekte für .NET Core, die auf das Metapaket Microsoft.AspNetCore.App verweisen, enthalten bereits das Microsoft.Extensions.Http
-Paket.
Es gibt mehrere Möglichkeiten IHttpClientFactory
in einer App zu verwenden:
Keine dieser Möglichkeiten ist einer anderen überlegen. Der beste Ansatz richtet sich nach den Einschränkungen der App.
IHttpClientFactory
kann durch Aufrufen der Erweiterungsmethode AddHttpClient
auf IServiceCollection
in der Methode Startup.ConfigureServices
registriert werden.
services.AddHttpClient();
Nach der Registrierung kann Code IHttpClientFactory
überall akzeptieren, wo Dienste mithilfe der Dependency Injection (DI) eingefügt werden können. IHttpClientFactory
kann zum Erstellen einer HttpClient
-Instanz verwendet werden:
public class BasicUsageModel : PageModel
{
private readonly IHttpClientFactory _clientFactory;
public IEnumerable<GitHubBranch> Branches { get; private set; }
public bool GetBranchesError { get; private set; }
public BasicUsageModel(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task OnGet()
{
var request = new HttpRequestMessage(HttpMethod.Get,
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches");
request.Headers.Add("Accept", "application/vnd.github.v3+json");
request.Headers.Add("User-Agent", "HttpClientFactory-Sample");
var client = _clientFactory.CreateClient();
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
Branches = await response.Content
.ReadAsAsync<IEnumerable<GitHubBranch>>();
}
else
{
GetBranchesError = true;
Branches = Array.Empty<GitHubBranch>();
}
}
}
IHttpClientFactory
auf diese Weise zu verwenden, ist eine gute Möglichkeit zum Umgestalten einer vorhandenen App. Dies hat keine Auswirkung auf die Verwendung von HttpClient
. An Stellen, in denen HttpClient
-Instanzen derzeit erstellt werden, können Sie diese Ereignisse mit einem Aufruf von CreateClient ersetzen.
Wenn eine App mehrere verschiedene Verwendungen von HttpClient
mit jeweils unterschiedlicher Konfiguration erfordert, ist die Verwendung von benannten Clients eine Option. Die Konfiguration einer benannten HttpClient
kann während der Registrierung in Startup.ConfigureServices
angegeben werden.
services.AddHttpClient("github", c =>
{
c.BaseAddress = new Uri("https://api.github.com/");
// Github API versioning
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
// Github requires a user-agent
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});
Im oben stehenden Code wird AddHttpClient
aufgerufen, und dabei wird der Name github angegeben. Auf diesen Client wird eine Standardkonfiguration angewendet, d. h. die Basisadresse und zwei Header, die für die Arbeit mit der GitHub-API erforderlich sind.
Wenn CreateClient
aufgerufen wird, wird jedes Mal eine neue Instanz von HttpClient
erstellt und die Konfigurationsaktion aufgerufen.
Zum Verarbeiten eines benannten Clients kann ein Zeichenfolgenparameter an CreateClient
übergeben werden. Geben Sie den Namen des zu erstellenden Clients an:
public class NamedClientModel : PageModel
{
private readonly IHttpClientFactory _clientFactory;
public IEnumerable<GitHubPullRequest> PullRequests { get; private set; }
public bool GetPullRequestsError { get; private set; }
public bool HasPullRequests => PullRequests.Any();
public NamedClientModel(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task OnGet()
{
var request = new HttpRequestMessage(HttpMethod.Get,
"repos/dotnet/AspNetCore.Docs/pulls");
var client = _clientFactory.CreateClient("github");
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
PullRequests = await response.Content
.ReadAsAsync<IEnumerable<GitHubPullRequest>>();
}
else
{
GetPullRequestsError = true;
PullRequests = Array.Empty<GitHubPullRequest>();
}
}
}
Im vorangehenden Code muss die Anforderung keinen Hostnamen angeben. Sie muss nur den Pfad übergeben, da die für den Client konfigurierte Basisadresse verwendet wird.
Typisierte Clients:
HttpClient
bereit. Zum Beispiel kann ein einzelner typisierter Client für einen einzelnen Back-End-Endpunkt verwendet werden, der jegliche Logik kapselt, die mit diesem Endpunkt interagiert.Ein typisierter Client akzeptiert einen HttpClient
-Parameter in seinem Konstruktor:
public class GitHubService
{
public HttpClient Client { get; }
public GitHubService(HttpClient client)
{
client.BaseAddress = new Uri("https://api.github.com/");
// GitHub API versioning
client.DefaultRequestHeaders.Add("Accept",
"application/vnd.github.v3+json");
// GitHub requires a user-agent
client.DefaultRequestHeaders.Add("User-Agent",
"HttpClientFactory-Sample");
Client = client;
}
public async Task<IEnumerable<GitHubIssue>> GetAspNetDocsIssues()
{
var response = await Client.GetAsync(
"/repos/dotnet/AspNetCore.Docs/issues?state=open&sort=created&direction=desc");
response.EnsureSuccessStatusCode();
var result = await response.Content
.ReadAsAsync<IEnumerable<GitHubIssue>>();
return result;
}
}
Im vorangehenden Code wird die Konfiguration in den typisierten Client verschoben. Das HttpClient
-Objekt wird als öffentliche Eigenschaft zur Verfügung gestellt. Es ist möglich, API-spezifische Methoden zu definieren, die die HttpClient
-Funktionalität zur Verfügung stellen. Die Methode GetAspNetDocsIssues
kapselt den Code, der für die Abfrage und Analyse des neuesten offenen Problems aus dem GitHub-Repository erforderlich ist.
Zum Registrieren eines typisierten Clients kann die generische Erweiterungsmethode AddHttpClient innerhalb von Startup.ConfigureServices
verwendet werden, die die typisierte Klasse angeben:
services.AddHttpClient<GitHubService>();
Der typisierte Client wird mit DI als „vorübergehend“ registriert. Der typisierte Client kann direkt eingefügt und verwendet werden:
public class TypedClientModel : PageModel
{
private readonly GitHubService _gitHubService;
public IEnumerable<GitHubIssue> LatestIssues { get; private set; }
public bool HasIssue => LatestIssues.Any();
public bool GetIssuesError { get; private set; }
public TypedClientModel(GitHubService gitHubService)
{
_gitHubService = gitHubService;
}
public async Task OnGet()
{
try
{
LatestIssues = await _gitHubService.GetAspNetDocsIssues();
}
catch(HttpRequestException)
{
GetIssuesError = true;
LatestIssues = Array.Empty<GitHubIssue>();
}
}
}
Die Konfiguration kann für einen typisierten Client während der Registrierung in Startup.ConfigureServices
angegeben werden, anstatt im Konstruktor des typisierten Clients, falls dies bevorzugt wird:
services.AddHttpClient<RepoService>(c =>
{
c.BaseAddress = new Uri("https://api.github.com/");
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});
Es ist möglich, HttpClient
vollständig in einem typisierten Client zu kapseln. Anstatt ihn als eine Eigenschaft zur Verfügung zu stellen, können öffentliche Methoden bereitgestellt werden, die die HttpClient
-Instanz intern aufrufen.
public class RepoService
{
// _httpClient isn't exposed publicly
private readonly HttpClient _httpClient;
public RepoService(HttpClient client)
{
_httpClient = client;
}
public async Task<IEnumerable<string>> GetRepos()
{
var response = await _httpClient.GetAsync("aspnet/repos");
response.EnsureSuccessStatusCode();
var result = await response.Content
.ReadAsAsync<IEnumerable<string>>();
return result;
}
}
Im vorangehenden Code wird HttpClient
als ein privates Feld gespeichert. Alle Zugriffe, die externe Aufrufe durchführen, durchlaufen die Methode GetRepos
.
IHttpClientFactory
kann in Verbindung mit anderen Bibliotheken von Drittanbietern verwendet werden, z.B. Refit. Refit ist eine REST-Bibliothek für .NET. Sie konvertiert REST-APIs in Live-Schnittstellen. Eine Implementierung der Schnittstelle wird dynamisch von RestService
generiert. HttpClient
wird für die externen HTTP-Aufrufe verwendet.
Eine Schnittstelle und eine Antwort werden definiert, um die externe API und die Antwort darzustellen:
public interface IHelloClient
{
[Get("/helloworld")]
Task<Reply> GetMessageAsync();
}
public class Reply
{
public string Message { get; set; }
}
Ein typisierter Client kann hinzugefügt werden. Refit wird zum Generieren der Implementierung verwendet:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("hello", c =>
{
c.BaseAddress = new Uri("http://localhost:5000");
})
.AddTypedClient(c => Refit.RestService.For<IHelloClient>(c));
services.AddMvc();
}
Die definierte Schnittstelle kann bei Bedarf mit der von DI und Refit bereitgestellten Implementierung verwendet werden:
[ApiController]
public class ValuesController : ControllerBase
{
private readonly IHelloClient _client;
public ValuesController(IHelloClient client)
{
_client = client;
}
[HttpGet("/")]
public async Task<ActionResult<Reply>> Index()
{
return await _client.GetMessageAsync();
}
}
HttpClient
enthält bereits das Konzept, Handler zu delegieren, die für ausgehende HTTP-Anforderungen miteinander verknüpft werden können. IHttpClientFactory
erleichtert das Definieren der Handler, die für jeden benannten Client angewendet werden. Die Registrierung und Verkettung von mehreren Handlern wird unterstützt, um eine Pipeline für die Middleware für ausgehende Anforderungen zu erstellen. Jeder dieser Handler kann vor und nach der ausgehenden Anforderung Aufgaben ausführen. Dieses Muster ähnelt der eingehenden Pipeline für Middleware in ASP.NET Core. Das Muster bietet einen Mechanismus zum Verwalten von übergreifenden Belangen bezüglich HTTP-Anforderungen, einschließlich der Zwischenspeicherung, Fehlerbehandlung, Serialisierung und Protokollierung.
Definieren Sie zum Erstellen eines Handlers eine von DelegatingHandler abgeleitete Klasse. Überschreiben Sie die Methode SendAsync
, um Code auszuführen, bevor die Anforderung an den nächsten Handler in der Pipeline übergeben wird:
public class ValidateHeaderHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (!request.Headers.Contains("X-API-KEY"))
{
return new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(
"You must supply an API key header called X-API-KEY")
};
}
return await base.SendAsync(request, cancellationToken);
}
}
Der vorangehende Code definiert einen einfachen Handler. Er überprüft, ob ein X-API-KEY
-Header in die Anforderung eingefügt wurde. Wenn der Header fehlt, kann er den HTTP-Aufruf vermeiden und eine geeignete Antwort zurückgeben.
Während der Registrierung kann mindestens ein Handler der Konfiguration eines HttpClient
hinzugefügt werden. Dieser Vorgang wird über die Erweiterungsmethoden von IHttpClientBuilder ermöglicht.
services.AddTransient<ValidateHeaderHandler>();
services.AddHttpClient("externalservice", c =>
{
// Assume this is an "external" service which requires an API KEY
c.BaseAddress = new Uri("https://localhost:5000/");
})
.AddHttpMessageHandler<ValidateHeaderHandler>();
Im vorangehenden Code ist ValidateHeaderHandler
mit DI registriert. Der Handler muss in DI als vorübergehender Dienst registriert sein, niemals bereichsbezogen. Wenn der Handler als bereichsbezogener Dienst registriert ist und alle Dienste, von denen der Handler abhängig ist, gelöscht werden können:
Nach der Registrierung kann AddHttpMessageHandler aufgerufen werden, wobei der Typ für den Handler übergeben wird.
Mehrere Handler können in der Reihenfolge registriert werden, in der sie ausgeführt werden sollen. Jeder Handler umschließt den nächsten Handler, bis der endgültige HttpClientHandler
die Anforderung ausführt:
services.AddTransient<SecureRequestHandler>();
services.AddTransient<RequestDataHandler>();
services.AddHttpClient("clientwithhandlers")
// This handler is on the outside and called first during the
// request, last during the response.
.AddHttpMessageHandler<SecureRequestHandler>()
// This handler is on the inside, closest to the request being
// sent.
.AddHttpMessageHandler<RequestDataHandler>();
Verwenden Sie einen der folgenden Ansätze, um den anforderungsspezifischen Zustand mit Meldungshandlern zu teilen:
HttpRequestMessage.Properties
an den Handler.IHttpContextAccessor
auf die aktuelle Anforderung zu.AsyncLocal
-Speicherobjekt, um die Daten zu übergeben.IHttpClientFactory
integriert mit der beliebten Drittanbieter-Bibliothek namens Polly. Polly ist eine umfassende Bibliothek für die Behandlung von beständigen und vorübergehenden Fehlern für .NET. Entwicklern wird damit ermöglicht, Richtlinien wie Wiederholungsrichtlinien, Trennschalterrichtlinien, Timeout-Richtlinien, Bulkhead Isolation-Richtlinien und Ausweichrichtlinien in einer flüssigen und threadsicheren Art und Weise auszudrücken.
Erweiterungsmethoden werden bereitgestellt, um die Verwendung von Polly-Richtlinien mit konfigurierten HttpClient
-Instanzen zu aktivieren. Die Polly-Erweiterungen:
Am häufigsten treten Fehler auf, wenn externe HTTP-Aufrufe vorübergehend sind. Eine praktische Erweiterungsmethode namens AddTransientHttpErrorPolicy
ist enthalten. Sie ermöglicht das Definieren von Richtlinien zum Behandeln vorübergehender Fehler. Richtlinien, die mit dieser Erweiterungsmethode konfiguriert wurden, behandeln HttpRequestException
, HTTP 5xx-Antworten und HTTP 408-Antworten.
Die AddTransientHttpErrorPolicy
-Erweiterung kann in Startup.ConfigureServices
verwendet werden. Die Erweiterung ermöglicht den Zugriff auf ein PolicyBuilder
-Objekt, das für die Fehlerbehandlung konfiguriert wurde und mögliche vorübergehende Fehler darstellt:
services.AddHttpClient<UnreliableEndpointCallerService>()
.AddTransientHttpErrorPolicy(p =>
p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));
Im vorangehenden Code wird eine WaitAndRetryAsync
-Richtlinie definiert. Anforderungsfehler werden bis zu dreimal mit einer Verzögerung von 600 ms wiederholt.
Es gibt zusätzliche Erweiterungsmethoden, die zum Hinzufügen von Polly-basierten Handlern verwendet werden können. Eine dieser Erweiterungen ist AddPolicyHandler
, die über mehrere Überladungen verfügt. Eine Überladung ermöglicht, dass die Anforderung beim Definieren der zu verwendenden Richtlinie überprüft werden kann:
var timeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(10));
var longTimeout = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(30));
services.AddHttpClient("conditionalpolicy")
// Run some code to select a policy based on the request
.AddPolicyHandler(request =>
request.Method == HttpMethod.Get ? timeout : longTimeout);
Im vorangehenden Code wird ein 10 Sekunden langer Timeout angewendet, wenn die ausgehende Anforderung eine HTTP GET-Anforderung ist. Für alle anderen HTTP-Methoden wird ein 30 Sekunden langer Timeout verwendet.
Es ist üblich, Polly-Richtlinien zu schachteln, um erweiterte Funktionen bereitzustellen:
services.AddHttpClient("multiplepolicies")
.AddTransientHttpErrorPolicy(p => p.RetryAsync(3))
.AddTransientHttpErrorPolicy(
p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
Im vorangehenden Beispiel werden zwei Handler hinzugefügt. Der Erste verwendet die AddTransientHttpErrorPolicy
-Erweiterung, um eine Wiederholungsrichtlinie hinzuzufügen. Fehlgeschlagene Anforderungen werden bis zu drei Mal wiederholt. Der zweite Aufruf von AddTransientHttpErrorPolicy
fügt eine Trennschalterrichtlinie hinzu. Weitere externe Anforderungen werden 30 Sekunden lang blockiert, wenn fünf Fehlversuche hintereinander stattfinden. Trennschalterrichtlinien sind zustandsbehaftet. Alle Aufrufe über diesen Client teilen den gleichen Schalterzustand.
Eine Methode zum Verwalten regelmäßig genutzter Richtlinien ist, Sie einmal zu definieren und mit PolicyRegistry
zu registrieren. Eine Erweiterungsmethode wird bereitgestellt, die einem Handler ermöglicht, mithilfe einer Richtlinie aus der Registrierung hinzugefügt zu werden:
var registry = services.AddPolicyRegistry();
registry.Add("regular", timeout);
registry.Add("long", longTimeout);
services.AddHttpClient("regulartimeouthandler")
.AddPolicyHandlerFromRegistry("regular");
Im oben stehenden Code werden zwei Richtlinien registriert, wenn PolicyRegistry
zu ServiceCollection
hinzugefügt wird. Damit eine Richtlinie aus der Registrierung verwendet werden kann, wird die Methode AddPolicyHandlerFromRegistry
verwendet. Dabei wird der Name der anzuwendenden Richtlinie übergeben.
Weitere Informationen zu IHttpClientFactory
und Polly-Integrationen finden Sie im Polly Wiki.
Bei jedem Aufruf von CreateClient
in der IHttpClientFactory
wird eine neue Instanz von HttpClient
zurückgegeben. Es gibt einen HttpMessageHandler pro benanntem Client. Die Factory verwaltet die Lebensdauer der HttpMessageHandler
-Instanzen.
IHttpClientFactory
legt die HttpMessageHandler
-Instanzen zusammen, die von der Factory zum Reduzieren des Ressourcenverbrauchs erstellt wurden. Eine HttpMessageHandler
-Instanz kann aus dem Pool wiederverwendet werden, wenn eine neue HttpClient
-Instanz erstellt wird und deren Lebensdauer noch nicht abgelaufen ist.
Das Zusammenlegen von Handlern ist ein wünschenswerter Vorgang, da jeder Handler in der Regel seine zugrunde liegenden HTTP-Verbindungen selbst verwaltet. Wenn mehr Handler als nötig erstellt werden, können Verzögerungen bei Verbindungen entstehen. Einige Handler halten Verbindungen auch unbegrenzt offen, was verhindert, dass der Handler auf DNS-Änderungen reagiert.
Die Standardlebensdauer von Handlern beträgt zwei Minuten. Der Standardwert kann für jeden benannten Client überschrieben werden. Rufen Sie SetHandlerLifetime auf dem bei der Erstellung des Clients zurückgegebenen IHttpClientBuilder
auf, um den Wert zu überschreiben:
services.AddHttpClient("extendedhandlerlifetime")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
Das Verwerfen des Client ist nicht erforderlich. Beim Verwerfen werden ausgehende Anforderungen abgebrochen, und es wird sichergestellt, dass die angegebene HttpClient
-Instanz nach dem Aufruf von Dispose nicht mehr verwendet werden kann. IHttpClientFactory
verfolgt von HttpClient
-Instanzen verwendete Ressourcen nach und verwirft sie. Die HttpClient
-Instanzen können im Allgemeinen als .NET-Objekte behandelt werden, die nicht verworfen werden müssen.
Das Beibehalten einer einzelnen HttpClient
-Instanz für einen langen Zeitraum ist ein allgemeines Muster, das vor der Einführung von IHttpClientFactory
verwendet wurde. Dieses Muster wird nach der Migration zu IHttpClientFactory
überflüssig.
Mit der Verwendung von IHttpClientFactory
in einer DI-fähigen App wird Folgendes vermieden:
HttpMessageHandler
-InstanzenHttpMessageHandler
-InstanzenEs gibt alternative Möglichkeiten zum Lösen des vorangehenden Problems mithilfe einer langlebigen SocketsHttpHandler-Instanz:
SocketsHttpHandler
-Instanz, wenn die App gestartet wird, und verwenden Sie diese für die Lebensdauer der App.HttpClient
-Instanzen mithilfe von new HttpClient(handler, disposeHandler: false)
.Diese Ansätze lösen die Ressourcenverwaltungsprobleme, die IHttpClientFactory
auf ähnliche Weise löst.
SocketsHttpHandler
gibt Verbindungen für HttpClient
-Instanzen frei. Durch diese Freigabe wird die Erschöpfung von Sockets vermieden.SocketsHttpHandler
wechselt die Verbindungen anhand von PooledConnectionLifetime
, um Probleme durch veraltetes DNS zu umgehen.Die zusammengelegten HttpMessageHandler
-Instanzen resultieren in der Freigabe von CookieContainer
-Objekten. Die unerwartete Freigabe von CookieContainer
-Objekten führt oft zu fehlerhaftem Code. Ziehen Sie für Apps, die Cookies erfordern, folgende Vorgehensweisen in Betracht:
IHttpClientFactory
Rufen Sie ConfigurePrimaryHttpMessageHandler auf, um die automatische cookieverarbeitung zu deaktivieren:
services.AddHttpClient("configured-disable-automatic-cookies")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
UseCookies = false,
};
});
Über IHttpClientFactory
erstellte Clients zeichnen Protokollmeldungen für alle Anforderungen auf. Aktivieren Sie die entsprechende Informationsebene in Ihrer Protokollierungskonfiguration, um die Standardprotokollmeldungen anzuzeigen. Zusätzliche Protokollierung, z.B. das Protokollieren von Anforderungsheadern, wird nur auf der Ablaufverfolgungsebene enthalten.
Die Protokollierungskategorie für jeden Client enthält den Namen des Clients. Ein Client namens MyNamedClient protokolliert beispielsweise Meldungen mit der Kategorie System.Net.Http.HttpClient.MyNamedClient.LogicalHandler
. Meldungen mit dem Suffix LogicalHandler treten außerhalb der Anforderungshandlerpipeline auf. In der Anforderung werden Meldungen protokolliert, bevor andere Handler in der Pipeline sie verarbeitet haben. In der Antwort werden Meldungen protokolliert, nachdem andere Handler in der Pipeline die Antwort empfangen haben.
Die Protokollierung tritt ebenfalls innerhalb der Anforderungshandlerpipeline auf. Im Beispiel MyNamedClient werden diese Meldungen für die Protokollkategorie System.Net.Http.HttpClient.MyNamedClient.ClientHandler
protokolliert. Dies findet für die Anforderung statt, nachdem alle anderen Handler ausgeführt wurden und bevor die Anforderung an das Netzwerk gesendet wird. Diese Protokollierung enthält den Zustand der Antwort in der Antwort, bevor sie an die Handlerpipeline zurückgegeben wird.
Das Aktivieren der Protokollierung außerhalb und innerhalb der Pipeline ermöglicht die Überprüfung der Änderungen, die durch andere Handler in der Pipeline erfolgt sind. Dies kann beispielsweise die Änderungen an Anforderungsheadern oder am Statuscode der Antwort enthalten.
Das Einschließen des Namens des Clients in der Protokollkategorie aktiviert bei Bedarf die Protokollfilterung für spezifische benannte Clients.
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 verwendet werden. Der Delegat wird verwendet, um den primären HttpMessageHandler
zu erstellen und konfigurieren, der von dem Client verwendet wird:
services.AddHttpClient("configured-inner-handler")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
AllowAutoRedirect = false,
UseDefaultCredentials = true
};
});
Fügen Sie in einer Konsolen-App dem Projekt die folgenden Paketverweise hinzu:
Im folgenden Beispiel:
MyService
erstellt eine Clientfactoryinstanz aus dem-Dienst, der zum Erstellen eines HttpClient
verwendet wird. HttpClient
wird zum Abrufen einer Webseite verwendet.GetPage
des Diensts wird ausgeführt, um die ersten 500 Zeichen des Webseiteninhalts in die Konsole zu schreiben. Weitere Informationen zum Aufrufen von Diensten über Program.Main
finden Sie unter Abhängigkeitsinjektion in ASP.NET Core.using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
class Program
{
static async Task<int> Main(string[] args)
{
var builder = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddHttpClient();
services.AddTransient<IMyService, MyService>();
}).UseConsoleLifetime();
var host = builder.Build();
try
{
var myService = host.Services.GetRequiredService<IMyService>();
var pageContent = await myService.GetPage();
Console.WriteLine(pageContent.Substring(0, 500));
}
catch (Exception ex)
{
var logger = host.Services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred.");
}
return 0;
}
public interface IMyService
{
Task<string> GetPage();
}
public class MyService : IMyService
{
private readonly IHttpClientFactory _clientFactory;
public MyService(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task<string> GetPage()
{
// Content from BBC One: Dr. Who website (©BBC)
var request = new HttpRequestMessage(HttpMethod.Get,
"https://www.bbc.co.uk/programmes/b006q2x0");
var client = _clientFactory.CreateClient();
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
else
{
return $"StatusCode: {response.StatusCode}";
}
}
}
}
Für die Headerweitergabe wird eine von der Community unterstützte Middleware verwendet, um HTTP-Header von eingehenden Anforderungen an ausgehende HTTP-Clientanforderungen weiterzugeben. So verwenden Sie die Headerweitergabe:
Erstellen Sie einen Verweis auf das von der Community unterstützte HeaderPropagation-Paket. ASp.NET Core 3.1 und höher unterstützen Microsoft.AspNetCore.HeaderPropagation.
Konfigurieren Sie in Startup
die Middleware und HttpClient
:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddHttpClient("MyForwardingClient").AddHeaderPropagation();
services.AddHeaderPropagation(options =>
{
options.Headers.Add("X-TraceId");
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseHeaderPropagation();
app.UseMvc();
}
Der Client schließt die konfigurierten Header für ausgehende Anforderungen ein:
var client = clientFactory.CreateClient("MyForwardingClient");
var response = client.GetAsync(...);
Feedback zu ASP.NET Core
ASP.NET Core ist ein Open Source-Projekt. Wählen Sie einen Link aus, um Feedback zu geben:
Ereignisse
Power BI DataViz Weltmeisterschaften
14. Feb., 16 Uhr - 31. März, 16 Uhr
Mit 4 Chancen, ein Konferenzpaket zu gewinnen und es zum LIVE Grand Finale in Las Vegas zu machen
Weitere Informationen