Stellen von HTTP-Anforderungen mithilfe von IHttpClientFactory in ASP.NET CoreMake HTTP requests using IHttpClientFactory in ASP.NET Core

Von Glenn Condron, Ryan Nowak, Steve Gordon, Rick Anderson und Kirk LarkinBy Glenn Condron, Ryan Nowak, Steve Gordon, Rick Anderson, and Kirk Larkin

IHttpClientFactory kann registriert und zum Konfigurieren und Erstellen von HttpClient-Instanzen in einer App verwendet werden.An IHttpClientFactory can be registered and used to configure and create HttpClient instances in an app. IHttpClientFactory bietet die folgenden Vorteile:IHttpClientFactory offers the following benefits:

  • Ein zentraler Ort für das Benennen und Konfigurieren logischer HttpClient-Instanzen wird damit geboten.Provides a central location for naming and configuring logical HttpClient instances. Ein Client namens github kann beispielsweise registriert und für den Zugriff auf GitHub konfiguriert werden.For example, a client named github could be registered and configured to access GitHub. Ein Standardclient kann für den allgemeinen Zugriff registriert werden.A default client can be registered for general access.
  • Das Konzept der ausgehenden Middleware wird über delegierende Handler in HttpClient in Code umgesetzt.Codifies the concept of outgoing middleware via delegating handlers in HttpClient. Außerdem werden Erweiterungen für auf Polly basierende Middleware bereitgestellt, um die delegierenden Handler in HttpClient zu nutzen.Provides extensions for Polly-based middleware to take advantage of delegating handlers in HttpClient.
  • Das Pooling und die Lebensdauer von zugrunde liegenden HttpClientMessageHandler-Instanzen werden verwaltet.Manages the pooling and lifetime of underlying HttpClientMessageHandler instances. Durch die automatische Verwaltung werden allgemeine DNS-Probleme (Domain Name System) vermieden, die bei der manuellen Verwaltung der Lebensdauer von HttpClient auftreten.Automatic management avoids common DNS (Domain Name System) problems that occur when manually managing HttpClient lifetimes.
  • Eine konfigurierbare Protokollierungsfunktion wird (über ILogger) für alle Anforderungen hinzugefügt, die über Clients gesendet werden, die von der Factory erstellt wurden.Adds a configurable logging experience (via ILogger) for all requests sent through clients created by the factory.

Zeigen Sie Beispielcode an, oder laden Sie diesen herunter (Vorgehensweise zum Herunterladen).View or download sample code (how to download).

Der Beispielcode in dieser Version des Themas verwendet System.Text.Json, um JSON-Inhalte zu deserialisieren, die in HTTP-Antworten zurückgegeben wurden.The sample code in this topic version uses System.Text.Json to deserialize JSON content returned in HTTP responses. Verwenden Sie bei Beispielen, die Json.NET und ReadAsAsync<T> verwenden, die Versionsauswahl, um eine 2.x-Version dieses Themas auszuwählen.For samples that use Json.NET and ReadAsAsync<T>, use the version selector to select a 2.x version of this topic.

VerbrauchsmusterConsumption patterns

Es gibt mehrere Möglichkeiten IHttpClientFactory in einer App zu verwenden:There are several ways IHttpClientFactory can be used in an app:

Der beste Ansatz richtet sich nach den Anforderungen der App.The best approach depends upon the app's requirements.

Grundlegende VerwendungBasic usage

IHttpClientFactory kann durch Aufrufen von AddHttpClient registriert werden:IHttpClientFactory can be registered by calling AddHttpClient:

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.An IHttpClientFactory can be requested using dependency injection (DI). Im folgenden Code wird IHttpClientFactory verwendet, um eine HttpClient-Instanz zu erstellen:The following code uses IHttpClientFactory to create an HttpClient instance:

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/aspnet/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.Using IHttpClientFactory like in the preceding example is a good way to refactor an existing app. Dies hat keine Auswirkung auf die Verwendung von HttpClient.It has no impact on how HttpClient is used. An Stellen, an denen HttpClient-Instanzen in einer vorhandenen App erstellt werden, können Sie diese Ereignisse mit Aufrufen von CreateClient ersetzen.In places where HttpClient instances are created in an existing app, replace those occurrences with calls to CreateClient.

Benannte ClientsNamed clients

Benannte Clients sind in folgenden Fällen eine gute Wahl:Named clients are a good choice when:

  • Die App erfordert viele verschiedene Verwendungen von HttpClient.The app requires many distinct uses of HttpClient.
  • Viele HttpClients verfügen über unterschiedliche Konfigurationen.Many HttpClients have different configuration.

Die Konfiguration eines benannten HttpClient kann während der Registrierung in Startup.ConfigureServices angegeben werden:Configuration for a named HttpClient can be specified during registration in Startup.ConfigureServices:

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:In the preceding code the client is configured with:

  • der Basisadresse https://api.github.com/The base address https://api.github.com/.
  • zwei Header, die für die Arbeit mit der GitHub-API erforderlich sindTwo headers required to work with the GitHub API.

CreateClientCreateClient

Jedes Mal, wenn CreateClient aufgerufen wird, geschieht Folgendes:Each time CreateClient is called:

  • Eine neue Instanz von HttpClient wird erstellt.A new instance of HttpClient is created.
  • Die Konfigurationsaktion wird aufgerufen.The configuration action is called.

Übergeben Sie für die Erstellung eines benannten Clients dessen Namen an CreateClient:To create a named client, pass its name into 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/aspnet/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.In the preceding code, the request doesn't need to specify a hostname. Der Code muss nur den Pfad übergeben, da die für den Client konfigurierte Basisadresse verwendet wird.The code can pass just the path, since the base address configured for the client is used.

Typisierte ClientsTyped clients

Typisierte Clients:Typed clients:

  • Stellen dieselben Funktionen wie benannte Clients bereit, ohne Zeichenfolgen als Schlüssel verwenden zu müssen.Provide the same capabilities as named clients without the need to use strings as keys.
  • Bieten Hilfe durch IntelliSense und Compiler beim Verarbeiten von Clients.Provides IntelliSense and compiler help when consuming clients.
  • Stellen einen einzelnen Ort zum Konfigurieren und Interagieren mit einem bestimmten HttpClient bereit.Provide a single location to configure and interact with a particular HttpClient. Beispielsweise kann ein einzelner typisierter Client für Folgendes verwendet werden:For example, a single typed client might be used:
    • für einen einzelnen Back-End-EndpunktFor a single backend endpoint.
    • um die gesamte Logik zu kapseln, die den Endpunkt behandeltTo encapsulate all logic dealing with the endpoint.
  • Funktionieren mit DI und können überall in der App eingefügt werden, wo sie benötigt werden.Work with DI and can be injected where required in the app.

Ein typisierter Client akzeptiert einen HttpClient-Parameter in seinem Konstruktor:A typed client accepts an HttpClient parameter in its constructor:

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/aspnet/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.If you would like to see code comments translated to languages other than English, let us know in this GitHub discussion issue.

Für den Code oben gilt:In the preceding code:

  • Die Konfiguration wird in den typisierten Client verschoben.The configuration is moved into the typed client.
  • Das HttpClient-Objekt wird als öffentliche Eigenschaft zur Verfügung gestellt.The HttpClient object is exposed as a public property.

Es können API-spezifische Methoden erstellt werden, die die HttpClient-Funktionalität verfügbar machen.API-specific methods can be created that expose HttpClient functionality. Die GetAspNetDocsIssues-Methode kapselt z. B. Code zum Abrufen offener Probleme.For example, the GetAspNetDocsIssues method encapsulates code to retrieve open issues.

Der folgende Code ruft AddHttpClient in Startup.ConfigureServices auf, um eine typisierte Clientklasse zu registrieren:The following code calls AddHttpClient in Startup.ConfigureServices to register a typed client class:

services.AddHttpClient<GitHubService>();

Der typisierte Client wird mit DI als „vorübergehend“ registriert.The typed client is registered as transient with DI. Im vorherigen Code registriert AddHttpClient GitHubService als vorübergehenden Dienst.In the preceding code, AddHttpClient registers GitHubService as a transient service. Diese Registrierung verwendet eine Factorymethode für folgende Aktionen:This registration uses a factory method to:

  1. Erstellen Sie eine Instanz von HttpClient:Create an instance of HttpClient.
  2. Erstellen einer GitHubService-Instanz, wobei die Instanz von HttpClient an ihren Konstrukt übergeben wirdCreate an instance of GitHubService, passing in the instance of HttpClient to its constructor.

Der typisierte Client kann direkt eingefügt und verwendet werden:The typed client can be injected and consumed directly:

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:The configuration for a typed client can be specified during registration in Startup.ConfigureServices, rather than in the typed client's constructor:

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.The HttpClient can be encapsulated within a typed client. Anstatt ihn als eine Eigenschaft zur Verfügung zu stellen, definieren Sie eine Methode, die die HttpClient-Instanz intern aufruft:Rather than exposing it as a property, define a method which calls the HttpClient instance internally:

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.In the preceding code, the HttpClient is stored in a private field. Der Zugriff auf HttpClient erfolgt durch die öffentliche GetRepos-Methode.Access to the HttpClient is by the public GetRepos method.

Generierte ClientsGenerated clients

IHttpClientFactory kann in Verbindung mit Bibliotheken von Drittanbietern verwendet werden, z. B. Refit.IHttpClientFactory can be used in combination with third-party libraries such as Refit. Refit ist eine REST-Bibliothek für .NET.Refit is a REST library for .NET. Sie konvertiert REST-APIs in Live-Schnittstellen.It converts REST APIs into live interfaces. Eine Implementierung der Schnittstelle wird dynamisch von RestService generiert. HttpClient wird für die externen HTTP-Aufrufe verwendet.An implementation of the interface is generated dynamically by the RestService, using HttpClient to make the external HTTP calls.

Eine Schnittstelle und eine Antwort werden definiert, um die externe API und die Antwort darzustellen:An interface and a reply are defined to represent the external API and its response:

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:A typed client can be added, using Refit to generate the implementation:

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:The defined interface can be consumed where necessary, with the implementation provided by DI and Refit:

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

Stellen von POST-, PUT- und DELETE-AnforderungenMake POST, PUT, and DELETE requests

In den vorangehenden Beispielen verwenden alle Anforderungen das GET-HTTP-Verb.In the preceding examples, all HTTP requests use the GET HTTP verb. HttpClient unterstützt ebenso andere HTTP-Verben, einschließlich der folgenden:HttpClient also supports other HTTP verbs, including:

  • POSTPOST
  • PUTPUT
  • DELETEDELETE
  • PATCHPATCH

Eine komplette Liste der unterstützten HTTP-Verben finden Sie unter HttpMethod.For a complete list of supported HTTP verbs, see HttpMethod.

Im folgenden Beispiel wird gezeigt, wie Sie eine HTTP-POST-Anforderung stellen:The following example shows how to make an HTTP POST request:

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:In the preceding code, the CreateItemAsync method:

  • Sie serialisiert den TodoItem-Parameter mithilfe von System.Text.Json in JSON.Serializes the TodoItem parameter to JSON using System.Text.Json. Dabei wird eine Instanz von JsonSerializerOptions verwenden, um den Serialisierungsprozess zu konfigurieren.This uses an instance of JsonSerializerOptions to configure the serialization process.
  • Sie erstellt eine Instanz von StringContent, um den serialisierten JSON-Code zum Senden im HTTP-Anforderungstext zu packen.Creates an instance of StringContent to package the serialized JSON for sending in the HTTP request's body.
  • Die Methode ruft PostAsync auf, um den JSON-Inhalt an die angegebene URL zu senden.Calls PostAsync to send the JSON content to the specified URL. Dies ist eine relative URL, die zu HttpClient.BaseAddress hinzugefügt wird.This is a relative URL that gets added to the HttpClient.BaseAddress.
  • Sie ruft EnsureSuccessStatusCode auf, um eine Ausnahme auszulösen, wenn der Antwortstatuscode nicht auf Erfolg hinweist.Calls EnsureSuccessStatusCode to throw an exception if the response status code does not indicate success.

HttpClient unterstützt auch andere Inhaltstypen.HttpClient also supports other types of content. Beispiel: MultipartContent und StreamContent.For example, MultipartContent and StreamContent. Eine komplette Liste der unterstützten Inhaltstypen finden Sie unter HttpContent.For a complete list of supported content, see HttpContent.

Das folgende Beispiel zeigt eine HTTP-PUT-Anforderung.The following example shows an HTTP PUT request:

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.The preceding code is very similar to the POST example. Die SaveItemAsync-Methode ruft PutAsync anstelle von PostAsync auf.The SaveItemAsync method calls PutAsync instead of PostAsync.

Das folgende Beispiel zeigt eine HTTP-DELETE-Anforderung.The following example shows an HTTP DELETE request:

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.In the preceding code, the DeleteItemAsync method calls DeleteAsync. Da HTTP-DELETE-Anforderungen normalerweise keinen Text enthalten, stellt die DeleteAsync-Methode keine Überladung bereit, die eine Instanz von HttpContent akzeptiert.Because HTTP DELETE requests typically contain no body, the DeleteAsync method doesn't provide an overload that accepts an instance of HttpContent.

Weitere Informationen zur Verwendung unterschiedlicher HTTP-Verben mit HttpClient finden Sie unter HttpClient.To learn more about using different HTTP verbs with HttpClient, see HttpClient.

Middleware für ausgehende AnforderungenOutgoing request middleware

HttpClient enthält das Konzept, Handler zu delegieren, die für ausgehende HTTP-Anforderungen miteinander verknüpft werden können.HttpClient has the concept of delegating handlers that can be linked together for outgoing HTTP requests. IHttpClientFactory:IHttpClientFactory:

  • Erleichtert das Definieren der Handler, die für jeden benannten Client angewendet werdenSimplifies defining the handlers to apply for each named client.

  • Unterstützt die Registrierung und Verkettung von mehreren Handlern, um eine Pipeline für die Middleware für ausgehende Anforderungen zu erstellen.Supports registration and chaining of multiple handlers to build an outgoing request middleware pipeline. Jeder dieser Handler kann vor und nach der ausgehenden Anforderung Aufgaben ausführen.Each of these handlers is able to perform work before and after the outgoing request. Dieses Muster:This pattern:

    • ähnelt der eingehenden Pipeline für Middleware in ASP.NET CoreIs similar to the inbound middleware pipeline in ASP.NET Core.

    • bietet einen Mechanismus zum Verwalten von übergreifenden Belangen bezüglich HTTP-Anforderungen, z. B.:Provides a mechanism to manage cross-cutting concerns around HTTP requests, such as:

      • Zwischenspeicherungcaching
      • Fehlerbehandlungerror handling
      • Serialisierungserialization
      • Protokollierunglogging

So erstellen Sie einen delegierenden Handler:To create a delegating handler:

  • Leiten Sie von DelegatingHandler ab.Derive from DelegatingHandler.
  • Überschreiben Sie SendAsync.Override SendAsync. Führen Sie Code aus, bevor die Anforderung an den nächsten Handler in der Pipeline übergeben wird:Execute code before passing the request to the next handler in the pipeline:
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.The preceding code checks if the X-API-KEY header is in the request. Wenn X-API-KEY fehlt, wird BadRequest zurückgegeben.If X-API-KEY is missing, BadRequest is returned.

Für eine HttpClient-Klasse mit Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandler kann mehr als ein Handler zur Konfiguration hinzugefügt werden:More than one handler can be added to the configuration for an HttpClient with Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandler:

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.In the preceding code, the ValidateHeaderHandler is registered with DI. Die IHttpClientFactory erstellt einen separaten DI-Bereich für jeden Handler.The IHttpClientFactory creates a separate DI scope for each handler. Handler können von Diensten eines beliebigen Bereichs abhängen.Handlers can depend upon services of any scope. Dienste, von denen Handler abhängig sind, werden verworfen, wenn der Handler verworfen wird.Services that handlers depend upon are disposed when the handler is disposed.

Nach der Registrierung kann AddHttpMessageHandler aufgerufen werden, was den Typ für den Handler übergibt.Once registered, AddHttpMessageHandler can be called, passing in the type for the handler.

Mehrere Handler können in der Reihenfolge registriert werden, in der sie ausgeführt werden sollen.Multiple handlers can be registered in the order that they should execute. Jeder Handler umschließt den nächsten Handler, bis der endgültige HttpClientHandler die Anforderung ausführt:Each handler wraps the next handler until the final HttpClientHandler executes the request:

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:Use one of the following approaches to share per-request state with message handlers:

Verwenden von Polly-basierten HandlernUse Polly-based handlers

IHttpClientFactory ist mit der Drittanbieterbibliothek Polly integriert.IHttpClientFactory integrates with the third-party library Polly. Polly ist eine umfassende Bibliothek für die Behandlung von beständigen und vorübergehenden Fehlern für .NET.Polly is a comprehensive resilience and transient fault-handling library for .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.It allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.

Erweiterungsmethoden werden bereitgestellt, um die Verwendung von Polly-Richtlinien mit konfigurierten HttpClient-Instanzen zu aktivieren.Extension methods are provided to enable the use of Polly policies with configured HttpClient instances. Die Polly-Erweiterungen unterstützen das Hinzufügen von Polly-basierten Handlern zu Clients.The Polly extensions support adding Polly-based handlers to clients. Polly erfordert das Microsoft.Extensions.Http.Polly-NuGet-Paket.Polly requires the Microsoft.Extensions.Http.Polly NuGet package.

Behandeln von vorrübergehenden FehlernHandle transient faults

Fehler treten normalerweise auf, wenn externe HTTP-Aufrufe vorübergehend sind.Faults typically occur when external HTTP calls are transient. AddTransientHttpErrorPolicy ermöglicht die Definition einer Richtlinie, um vorübergehende Fehler zu behandeln.AddTransientHttpErrorPolicy allows a policy to be defined to handle transient errors. Mit AddTransientHttpErrorPolicy konfigurierte Richtlinien behandeln die folgenden Antworten:Policies configured with AddTransientHttpErrorPolicy handle the following responses:

AddTransientHttpErrorPolicy ermöglicht den Zugriff auf ein PolicyBuilder-Objekt, das für die Fehlerbehandlung konfiguriert wurde und mögliche vorübergehende Fehler darstellt:AddTransientHttpErrorPolicy provides access to a PolicyBuilder object configured to handle errors representing a possible transient fault:

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.In the preceding code, a WaitAndRetryAsync policy is defined. Anforderungsfehler werden bis zu dreimal mit einer Verzögerung von 600 ms wiederholt.Failed requests are retried up to three times with a delay of 600 ms between attempts.

Dynamisches Auswählen von RichtlinienDynamically select policies

Erweiterungsmethoden werden zum Hinzufügen von Polly-basierten Handlern bereitgestellt, z. B. AddPolicyHandler.Extension methods are provided to add Polly-based handlers, for example, AddPolicyHandler. Der folgende AddPolicyHandler-Überladung prüft die Anforderung, um zu entscheiden, welche Richtlinie angewendet werden soll:The following AddPolicyHandler overload inspects the request to decide which policy to apply:

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.In the preceding code, if the outgoing request is an HTTP GET, a 10-second timeout is applied. Für alle anderen HTTP-Methoden wird ein 30 Sekunden langer Timeout verwendet.For any other HTTP method, a 30-second timeout is used.

Hinzufügen mehrerer Polly-HandlerAdd multiple Polly handlers

Es ist üblich, Polly-Richtlinien zu schachteln:It's common to nest Polly policies:

services.AddHttpClient("multiplepolicies")
    .AddTransientHttpErrorPolicy(p => p.RetryAsync(3))
    .AddTransientHttpErrorPolicy(
        p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));

Im vorherigen Beispiel:In the preceding example:

  • Zwei Handler werden hinzugefügt.Two handlers are added.
  • Der erste Handler verwendet AddTransientHttpErrorPolicy, um eine Wiederholungsrichtlinie hinzuzufügen.The first handler uses AddTransientHttpErrorPolicy to add a retry policy. Fehlgeschlagene Anforderungen werden bis zu drei Mal wiederholt.Failed requests are retried up to three times.
  • Der zweite AddTransientHttpErrorPolicy-Aufruf fügt eine Trennschalterrichtlinie hinzu.The second AddTransientHttpErrorPolicy call adds a circuit breaker policy. Weitere externe Anforderungen werden 30 Sekunden lang blockiert, wenn fünf Fehlversuche hintereinander stattfinden.Further external requests are blocked for 30 seconds if 5 failed attempts occur sequentially. Trennschalterrichtlinien sind zustandsbehaftet.Circuit breaker policies are stateful. Alle Aufrufe über diesen Client teilen den gleichen Schalterzustand.All calls through this client share the same circuit state.

Hinzufügen von Richtlinien aus der Polly-RegistrierungAdd policies from the Polly registry

Eine Methode zum Verwalten regelmäßig genutzter Richtlinien ist, Sie einmal zu definieren und mit PolicyRegistry zu registrieren.An approach to managing regularly used policies is to define them once and register them with a PolicyRegistry.

Im folgenden Code wird Folgendes ausgeführt:In the following code:

  • Die Richtlinien „regular“ und „long“ werden hinzugefügt.The "regular" and "long" policies are added.
  • AddPolicyHandlerFromRegistry fügt die Richtlinien „regular“ und „long“ aus der Registrierung hinzu.AddPolicyHandlerFromRegistry adds the "regular" and "long" policies from the registry.
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.For more information on IHttpClientFactory and Polly integrations, see the Polly wiki.

HttpClient und die Verwaltung der LebensdauerHttpClient and lifetime management

Bei jedem Aufruf von CreateClient in der IHttpClientFactory wird eine neue Instanz von HttpClient zurückgegeben.A new HttpClient instance is returned each time CreateClient is called on the IHttpClientFactory. Pro benannter Client wird ein HttpMessageHandler erstellt.An HttpMessageHandler is created per named client. Die Factory verwaltet die Lebensdauer der HttpMessageHandler-Instanzen.The factory manages the lifetimes of the HttpMessageHandler instances.

IHttpClientFactory legt die HttpMessageHandler-Instanzen zusammen, die von der Factory zum Reduzieren des Ressourcenverbrauchs erstellt wurden.IHttpClientFactory pools the HttpMessageHandler instances created by the factory to reduce resource consumption. Eine HttpMessageHandler-Instanz kann aus dem Pool wiederverwendet werden, wenn eine neue HttpClient-Instanz erstellt wird und deren Lebensdauer noch nicht abgelaufen ist.An HttpMessageHandler instance may be reused from the pool when creating a new HttpClient instance if its lifetime hasn't expired.

Das Zusammenlegen von Handlern ist ein wünschenswerter Vorgang, da jeder Handler in der Regel seine zugrunde liegenden HTTP-Verbindungen selbst verwaltet.Pooling of handlers is desirable as each handler typically manages its own underlying HTTP connections. Wenn mehr Handler als nötig erstellt werden, können Verzögerungen bei Verbindungen entstehen.Creating more handlers than necessary can result in connection delays. Einige Handler halten Verbindungen auch unbegrenzt offen, was verhindert, dass der Handler auf DNS-Änderungen (Domain Name System) reagiert.Some handlers also keep connections open indefinitely, which can prevent the handler from reacting to DNS (Domain Name System) changes.

Die Standardlebensdauer von Handlern beträgt zwei Minuten.The default handler lifetime is two minutes. Der Standardwert kann für jeden benannten Client überschrieben werden:The default value can be overridden on a per named client basis:

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.HttpClient instances can generally be treated as .NET objects not requiring disposal. 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.Disposal cancels outgoing requests and guarantees the given HttpClient instance can't be used after calling Dispose. IHttpClientFactory verfolgt von HttpClient-Instanzen verwendete Ressourcen nach und verwirft sie.IHttpClientFactory tracks and disposes resources used by HttpClient instances.

Das Beibehalten einer einzelnen HttpClient-Instanz für einen langen Zeitraum ist ein allgemeines Muster, das vor der Einführung von IHttpClientFactory verwendet wurde.Keeping a single HttpClient instance alive for a long duration is a common pattern used before the inception of IHttpClientFactory. Dieses Muster wird nach der Migration zu IHttpClientFactory überflüssig.This pattern becomes unnecessary after migrating to IHttpClientFactory.

Alternativen zu IHttpClientFactoryAlternatives to IHttpClientFactory

Mit der Verwendung von IHttpClientFactory in einer DI-fähigen App wird Folgendes vermieden:Using IHttpClientFactory in a DI-enabled app avoids:

  • Probleme mit der Ressourcenauslastung durch Zusammenlegen von HttpMessageHandler-InstanzenResource exhaustion problems by pooling HttpMessageHandler instances.
  • Probleme durch veraltetes DNS durch regelmäßigen Wechsel von HttpMessageHandler-InstanzenStale DNS problems by cycling HttpMessageHandler instances at regular intervals.

Es gibt alternative Möglichkeiten zum Lösen des vorangehenden Problems mithilfe einer langlebigen SocketsHttpHandler-Instanz:There are alternative ways to solve the preceding problems using a long-lived SocketsHttpHandler instance.

  • Erstellen Sie eine SocketsHttpHandler-Instanz, wenn die App gestartet wird, und verwenden Sie diese für die Lebensdauer der App.Create an instance of SocketsHttpHandler when the app starts and use it for the life of the app.
  • Konfigurieren Sie einen geeigneten Wert für PooledConnectionLifetime basierend auf den DNS-Aktualisierungszeiten.Configure PooledConnectionLifetime to an appropriate value based on DNS refresh times.
  • Erstellen Sie bei Bedarf HttpClient-Instanzen mithilfe von new HttpClient(handler, disposeHandler: false).Create HttpClient instances using new HttpClient(handler, disposeHandler: false) as needed.

Diese Ansätze lösen die Ressourcenverwaltungsprobleme, die IHttpClientFactory auf ähnliche Weise löst.The preceding approaches solve the resource management problems that IHttpClientFactory solves in a similar way.

  • SocketsHttpHandler gibt Verbindungen für HttpClient-Instanzen frei.The SocketsHttpHandler shares connections across HttpClient instances. Durch diese Freigabe wird die Erschöpfung von Sockets vermieden.This sharing prevents socket exhaustion.
  • SocketsHttpHandler wechselt die Verbindungen anhand von PooledConnectionLifetime, um Probleme durch veraltetes DNS zu umgehen.The SocketsHttpHandler cycles connections according to PooledConnectionLifetime to avoid stale DNS problems.

CookiesCookies

Die zusammengelegten HttpMessageHandler-Instanzen resultieren in der Freigabe von CookieContainer-Objekten.The pooled HttpMessageHandler instances results in CookieContainer objects being shared. Die unerwartete Freigabe von CookieContainer-Objekten führt oft zu fehlerhaftem Code.Unanticipated CookieContainer object sharing often results in incorrect code. Ziehen Sie für Apps, die Cookies erfordern, folgende Vorgehensweisen in Betracht:For apps that require cookies, consider either:

  • Deaktivieren der automatischen CookieverarbeitungDisabling automatic cookie handling
  • Vermeiden der Verwendung von IHttpClientFactoryAvoiding IHttpClientFactory

Rufen Sie ConfigurePrimaryHttpMessageHandler auf, um die automatische Cookieverarbeitung zu deaktivieren:Call ConfigurePrimaryHttpMessageHandler to disable automatic cookie handling:

services.AddHttpClient("configured-disable-automatic-cookies")
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        return new SocketsHttpHandler()
        {
            UseCookies = false,
        };
    });

ProtokollierungLogging

Über IHttpClientFactory erstellte Clients zeichnen Protokollmeldungen für alle Anforderungen auf.Clients created via IHttpClientFactory record log messages for all requests. Aktivieren Sie die entsprechende Informationsebene in der Protokollierungskonfiguration, um die Standardprotokollmeldungen anzuzeigen.Enable the appropriate information level in the logging configuration to see the default log messages. Zusätzliche Protokollierung, z.B. das Protokollieren von Anforderungsheadern, wird nur auf der Ablaufverfolgungsebene enthalten.Additional logging, such as the logging of request headers, is only included at trace level.

Die Protokollierungskategorie für jeden Client enthält den Namen des Clients.The log category used for each client includes the name of the client. Ein Client namens MyNamedClient protokolliert z. B. Nachrichten mit der Kategorie „System.Net.Http.HttpClient.MyNamedClient.LogicalHandler“.A client named MyNamedClient, for example, logs messages with a category of "System.Net.Http.HttpClient.MyNamedClient.LogicalHandler". Meldungen mit dem Suffix LogicalHandler treten außerhalb der Anforderungshandlerpipeline auf.Messages suffixed with LogicalHandler occur outside the request handler pipeline. In der Anforderung werden Meldungen protokolliert, bevor andere Handler in der Pipeline sie verarbeitet haben.On the request, messages are logged before any other handlers in the pipeline have processed it. In der Antwort werden Meldungen protokolliert, nachdem andere Handler in der Pipeline die Antwort empfangen haben.On the response, messages are logged after any other pipeline handlers have received the response.

Die Protokollierung tritt ebenfalls innerhalb der Anforderungshandlerpipeline auf.Logging also occurs inside the request handler pipeline. Im Beispiel von MyNamedClient werden diese Nachrichten mit der Protokollkategorie „System.Net.Http.HttpClient.MyNamedClient.ClientHandler“ protokolliert.In the MyNamedClient example, those messages are logged with the log category "System.Net.Http.HttpClient.MyNamedClient.ClientHandler". Für die Anforderung erfolgt dies, nachdem alle anderen Handler ausgeführt wurden und bevor die Anforderung gesendet wird.For the request, this occurs after all other handlers have run and immediately before the request is sent. Diese Protokollierung enthält den Zustand der Antwort in der Antwort, bevor sie an die Handlerpipeline zurückgegeben wird.On the response, this logging includes the state of the response before it passes back through the handler pipeline.

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.Enabling logging outside and inside the pipeline enables inspection of the changes made by the other pipeline handlers. Dies kann die Änderungen an Anforderungsheadern oder am Statuscode der Antwort enthalten.This may include changes to request headers or to the response status code.

Das Einschließen des Namens des Clients in der Protokollkategorie aktiviert die Protokollfilterung für spezifische benannte Clients.Including the name of the client in the log category enables log filtering for specific named clients.

Konfigurieren von HttpMessageHandlerConfigure the HttpMessageHandler

Es kann notwendig sein, die Konfiguration des inneren von einem Client verwendeten HttpMessageHandler zu steuern.It may be necessary to control the configuration of the inner HttpMessageHandler used by a client.

IHttpClientBuilder wird zurückgegeben, wenn benannte oder typisierte Clients hinzugefügt werden.An IHttpClientBuilder is returned when adding named or typed clients. Die Erweiterungsmethode ConfigurePrimaryHttpMessageHandler kann zum Definieren eines Delegaten verwendet werden.The ConfigurePrimaryHttpMessageHandler extension method can be used to define a delegate. Der Delegat wird verwendet, um den primären HttpMessageHandler zu erstellen und konfigurieren, der von dem Client verwendet wird:The delegate is used to create and configure the primary HttpMessageHandler used by that client:

public void ConfigureServices(IServiceCollection services)
{            
    services.AddHttpClient("configured-inner-handler")
        .ConfigurePrimaryHttpMessageHandler(() =>
        {
            return new HttpClientHandler()
            {
                AllowAutoRedirect = false,
                UseDefaultCredentials = true
            };
        });

    // Remaining code deleted for brevity.

Verwenden von IHttpClientFactory in einer Konsolen-AppUse IHttpClientFactory in a console app

Fügen Sie in einer Konsolen-App dem Projekt die folgenden Paketverweise hinzu:In a console app, add the following package references to the project:

Im folgenden Beispiel:In the following example:

  • IHttpClientFactory ist im Dienstcontainer des generischen Hosts registriert.IHttpClientFactory is registered in the Generic Host's service container.
  • MyService erstellt eine Clientfactoryinstanz aus dem-Dienst, der zum Erstellen eines HttpClientverwendet wird.MyService creates a client factory instance from the service, which is used to create an HttpClient. HttpClient wird zum Abrufen einer Webseite verwendet.HttpClient is used to retrieve a webpage.
  • Main erstellt einen Bereich, um die GetPage-Methode des Diensts auszuführen und die ersten 500 Zeichen des Webseiteninhalts in die Konsole zu schreiben.Main creates a scope to execute the service's GetPage method and write the first 500 characters of the webpage content to the console.
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();

        using (var serviceScope = host.Services.CreateScope())
        {
            var services = serviceScope.ServiceProvider;

            try
            {
                var myService = services.GetRequiredService<IMyService>();
                var pageContent = await myService.GetPage();

                Console.WriteLine(pageContent.Substring(0, 500));
            }
            catch (Exception ex)
            {
                var logger = 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}";
            }
        }
    }
}

Middleware für HeaderweitergabeHeader propagation middleware

Für die Headerweitergabe wird eine ASP.NET Core-Middleware verwendet, um HTTP-Header von eingehenden Anforderungen an ausgehende HTTP-Clientanforderungen weiterzugeben.Header propagation is an ASP.NET Core middleware to propagate HTTP headers from the incoming request to the outgoing HTTP Client requests. So verwenden Sie die Headerweitergabe:To use header propagation:

  • Erstellen Sie einen Verweis auf das Microsoft.AspNetCore.HeaderPropagation-Paket.Reference the Microsoft.AspNetCore.HeaderPropagation package.

  • Konfigurieren Sie in Startup die Middleware und HttpClient:Configure the middleware and HttpClient in Startup:

    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:The client includes the configured headers on outbound requests:

    var client = clientFactory.CreateClient("MyForwardingClient");
    var response = client.GetAsync(...);
    

Zusätzliche RessourcenAdditional resources

Von Glenn Condron, Ryan Nowak, und Steve GordonBy Glenn Condron, Ryan Nowak, and Steve Gordon

IHttpClientFactory kann registriert und zum Konfigurieren und Erstellen von HttpClient-Instanzen in einer App verwendet werden.An IHttpClientFactory can be registered and used to configure and create HttpClient instances in an app. Dies hat folgende Vorteile:It offers the following benefits:

  • Ein zentraler Ort für das Benennen und Konfigurieren logischer HttpClient-Instanzen wird damit geboten.Provides a central location for naming and configuring logical HttpClient instances. Ein GitHub-Client kann beispielsweise registriert werden und so konfiguriert werden, um auf GitHub zuzugreifen.For example, a github client can be registered and configured to access GitHub. Ein Standard-Client kann für andere Zwecke registriert werden.A default client can be registered for other purposes.
  • Das Konzept der ausgehenden Middleware wird über delegierende Handler in HttpClient in Code umgesetzt. Außerdem werden Erweiterungen für auf Polly basierende Middleware bereitgestellt, die dies nutzen.Codifies the concept of outgoing middleware via delegating handlers in HttpClient and provides extensions for Polly-based middleware to take advantage of that.
  • Das Pooling und die Lebensdauer von zugrunde liegenden HttpClientMessageHandler-Instanzen werden verwaltet, um gängige DNS-Probleme zu vermeiden, die bei der manuellen Verwaltung der HttpClient-Lebensdauer auftreten.Manages the pooling and lifetime of underlying HttpClientMessageHandler instances to avoid common DNS problems that occur when manually managing HttpClient lifetimes.
  • Eine konfigurierbare Protokollierungsfunktion wird (über ILogger) für alle Anforderungen hinzugefügt, die über Clients gesendet werden, die von der Factory erstellt wurden.Adds a configurable logging experience (via ILogger) for all requests sent through clients created by the factory.

Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)View or download sample code (how to download)

VerbrauchsmusterConsumption patterns

Es gibt mehrere Möglichkeiten IHttpClientFactory in einer App zu verwenden:There are several ways IHttpClientFactory can be used in an app:

Keine dieser Möglichkeiten ist einer anderen überlegen.None of them are strictly superior to another. Der beste Ansatz richtet sich nach den Einschränkungen der App.The best approach depends upon the app's constraints.

Grundlegende VerwendungBasic usage

IHttpClientFactory kann durch Aufrufen der Erweiterungsmethode AddHttpClient auf IServiceCollection in der Methode Startup.ConfigureServices registriert werden.The IHttpClientFactory can be registered by calling the AddHttpClient extension method on the IServiceCollection, inside the Startup.ConfigureServices method.

services.AddHttpClient();

Nach der Registrierung kann Code IHttpClientFactory überall akzeptieren, wo Dienste mithilfe der Dependency Injection (DI) eingefügt werden können.Once registered, code can accept an IHttpClientFactory anywhere services can be injected with dependency injection (DI). IHttpClientFactory kann zum Erstellen einer HttpClient-Instanz verwendet werden:The IHttpClientFactory can be used to create an HttpClient instance:

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/aspnet/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.Using IHttpClientFactory in this fashion is a good way to refactor an existing app. Dies hat keine Auswirkung auf die Verwendung von HttpClient.It has no impact on the way HttpClient is used. An Stellen, in denen HttpClient-Instanzen derzeit erstellt werden, können Sie diese Ereignisse mit einem Aufruf von CreateClient ersetzen.In places where HttpClient instances are currently created, replace those occurrences with a call to CreateClient.

Benannte ClientsNamed clients

Wenn eine App mehrere verschiedene Verwendungen von HttpClient mit jeweils unterschiedlicher Konfiguration erfordert, ist die Verwendung von benannten Clients eine Option.If an app requires many distinct uses of HttpClient, each with a different configuration, an option is to use named clients. Die Konfiguration einer benannten HttpClient kann während der Registrierung in Startup.ConfigureServices angegeben werden.Configuration for a named HttpClient can be specified during registration in Startup.ConfigureServices.

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.In the preceding code, AddHttpClient is called, providing the name github. Für diesen Client wird Standardkonfiguration angewendet, d.h. die Basisadresse und zwei Header, die erforderlich sind, um mit der GitHub-API zu funktionieren.This client has some default configuration applied—namely the base address and two headers required to work with the GitHub API.

Wenn CreateClient aufgerufen wird, wird jedes Mal eine neue Instanz von HttpClient erstellt und die Konfigurationsaktion aufgerufen.Each time CreateClient is called, a new instance of HttpClient is created and the configuration action is called.

Zum Verarbeiten eines benannten Clients kann ein Zeichenfolgenparameter an CreateClient übergeben werden.To consume a named client, a string parameter can be passed to CreateClient. Geben Sie den Namen des zu erstellenden Clients an:Specify the name of the client to be created:

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/aspnet/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.In the preceding code, the request doesn't need to specify a hostname. Sie muss nur den Pfad übergeben, da die für den Client konfigurierte Basisadresse verwendet wird.It can pass just the path, since the base address configured for the client is used.

Typisierte ClientsTyped clients

Typisierte Clients:Typed clients:

  • Stellen dieselben Funktionen wie benannte Clients bereit, ohne Zeichenfolgen als Schlüssel verwenden zu müssen.Provide the same capabilities as named clients without the need to use strings as keys.
  • Bieten Hilfe durch IntelliSense und Compiler beim Verarbeiten von Clients.Provides IntelliSense and compiler help when consuming clients.
  • Stellen einen einzelnen Ort zum Konfigurieren und Interagieren mit einem bestimmten HttpClient bereit.Provide a single location to configure and interact with a particular HttpClient. 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.For example, a single typed client might be used for a single backend endpoint and encapsulate all logic dealing with that endpoint.
  • Funktionieren mit DI und können überall in Ihrer App eingefügt werden, wo sie benötigt werden.Work with DI and can be injected where required in your app.

Ein typisierter Client akzeptiert einen HttpClient-Parameter in seinem Konstruktor:A typed client accepts an HttpClient parameter in its constructor:

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/aspnet/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.In the preceding code, the configuration is moved into the typed client. Das HttpClient-Objekt wird als öffentliche Eigenschaft zur Verfügung gestellt.The HttpClient object is exposed as a public property. Es ist möglich, API-spezifische Methoden zu definieren, die die HttpClient-Funktionalität zur Verfügung stellen.It's possible to define API-specific methods that expose HttpClient functionality. Die Methode GetAspNetDocsIssues kapselt den Code, der für die Abfrage und Analyse des neuesten offenen Problems aus dem GitHub-Repository erforderlich ist.The GetAspNetDocsIssues method encapsulates the code needed to query for and parse out the latest open issues from a GitHub repository.

Zum Registrieren eines typisierten Clients kann die generische Erweiterungsmethode AddHttpClient innerhalb von Startup.ConfigureServices verwendet werden, die die typisierte Klasse angeben:To register a typed client, the generic AddHttpClient extension method can be used within Startup.ConfigureServices, specifying the typed client class:

services.AddHttpClient<GitHubService>();

Der typisierte Client wird mit DI als „vorübergehend“ registriert.The typed client is registered as transient with DI. Der typisierte Client kann direkt eingefügt und verwendet werden:The typed client can be injected and consumed directly:

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:If preferred, the configuration for a typed client can be specified during registration in Startup.ConfigureServices, rather than in the typed client's constructor:

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.It's possible to entirely encapsulate the HttpClient within a typed client. Anstatt ihn als eine Eigenschaft zur Verfügung zu stellen, können öffentliche Methoden bereitgestellt werden, die die HttpClient-Instanz intern aufrufen.Rather than exposing it as a property, public methods can be provided which call the HttpClient instance internally.

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.In the preceding code, the HttpClient is stored as a private field. Alle Zugriffe, die externe Aufrufe durchführen, durchlaufen die Methode GetRepos.All access to make external calls goes through the GetRepos method.

Generierte ClientsGenerated clients

IHttpClientFactory kann in Verbindung mit anderen Bibliotheken von Drittanbietern verwendet werden, z.B. Refit.IHttpClientFactory can be used in combination with other third-party libraries such as Refit. Refit ist eine REST-Bibliothek für .NET.Refit is a REST library for .NET. Sie konvertiert REST-APIs in Live-Schnittstellen.It converts REST APIs into live interfaces. Eine Implementierung der Schnittstelle wird dynamisch von RestService generiert. HttpClient wird für die externen HTTP-Aufrufe verwendet.An implementation of the interface is generated dynamically by the RestService, using HttpClient to make the external HTTP calls.

Eine Schnittstelle und eine Antwort werden definiert, um die externe API und die Antwort darzustellen:An interface and a reply are defined to represent the external API and its response:

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:A typed client can be added, using Refit to generate the implementation:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient("hello", c =>
    {
        c.BaseAddress = new Uri("https://localhost:5001");
    })
    .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:The defined interface can be consumed where necessary, with the implementation provided by DI and Refit:

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

Middleware für ausgehende AnforderungenOutgoing request middleware

HttpClient enthält bereits das Konzept, Handler zu delegieren, die für ausgehende HTTP-Anforderungen miteinander verknüpft werden können.HttpClient already has the concept of delegating handlers that can be linked together for outgoing HTTP requests. IHttpClientFactory erleichtert das Definieren der Handler, die für jeden benannten Client angewendet werden.The IHttpClientFactory makes it easy to define the handlers to apply for each named client. Die Registrierung und Verkettung von mehreren Handlern wird unterstützt, um eine Pipeline für die Middleware für ausgehende Anforderungen zu erstellen.It supports registration and chaining of multiple handlers to build an outgoing request middleware pipeline. Jeder dieser Handler kann vor und nach der ausgehenden Anforderung Aufgaben ausführen.Each of these handlers is able to perform work before and after the outgoing request. Dieses Muster ähnelt der eingehenden Pipeline für Middleware in ASP.NET Core.This pattern is similar to the inbound middleware pipeline 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.The pattern provides a mechanism to manage cross-cutting concerns around HTTP requests, including caching, error handling, serialization, and logging.

Definieren Sie zum Erstellen eines Handlers eine von DelegatingHandler abgeleitete Klasse.To create a handler, define a class deriving from DelegatingHandler. Überschreiben Sie die Methode SendAsync, um Code auszuführen, bevor die Anforderung an den nächsten Handler in der Pipeline übergeben wird:Override the SendAsync method to execute code before passing the request to the next handler in the pipeline:

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.The preceding code defines a basic handler. Er überprüft, ob ein X-API-KEY-Header in die Anforderung eingefügt wurde.It checks to see if an X-API-KEY header has been included on the request. Wenn der Header fehlt, kann er den HTTP-Aufruf vermeiden und eine geeignete Antwort zurückgeben.If the header is missing, it can avoid the HTTP call and return a suitable response.

Während der Registrierung kann mindestens ein Handler der Konfiguration eines HttpClient hinzugefügt werden.During registration, one or more handlers can be added to the configuration for an HttpClient. Dieser Vorgang wird über die Erweiterungsmethoden von IHttpClientBuilder ermöglicht.This task is accomplished via extension methods on the IHttpClientBuilder.

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.In the preceding code, the ValidateHeaderHandler is registered with DI. Die IHttpClientFactory erstellt einen separaten DI-Bereich für jeden Handler.The IHttpClientFactory creates a separate DI scope for each handler. Handler können von Diensten eines beliebigen Bereichs abhängen.Handlers are free to depend upon services of any scope. Dienste, von denen Handler abhängig sind, werden verworfen, wenn der Handler verworfen wird.Services that handlers depend upon are disposed when the handler is disposed.

Nach der Registrierung kann AddHttpMessageHandler aufgerufen werden, was den Typ für den Handler übergibt.Once registered, AddHttpMessageHandler can be called, passing in the type for the handler.

Mehrere Handler können in der Reihenfolge registriert werden, in der sie ausgeführt werden sollen.Multiple handlers can be registered in the order that they should execute. Jeder Handler umschließt den nächsten Handler, bis der endgültige HttpClientHandler die Anforderung ausführt:Each handler wraps the next handler until the final HttpClientHandler executes the request:

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:Use one of the following approaches to share per-request state with message handlers:

  • Übergeben Sie Daten mit HttpRequestMessage.Properties an den Handler.Pass data into the handler using HttpRequestMessage.Properties.
  • Greifen Sie mit IHttpContextAccessor auf die aktuelle Anforderung zu.Use IHttpContextAccessor to access the current request.
  • Erstellen Sie ein benutzerdefiniertes AsyncLocal-Speicherobjekt, um die Daten zu übergeben.Create a custom AsyncLocal storage object to pass the data.

Verwenden von Polly-basierten HandlernUse Polly-based handlers

IHttpClientFactory integriert mit der beliebten Drittanbieter-Bibliothek namens Polly.IHttpClientFactory integrates with a popular third-party library called Polly. Polly ist eine umfassende Bibliothek für die Behandlung von beständigen und vorübergehenden Fehlern für .NET.Polly is a comprehensive resilience and transient fault-handling library for .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.It allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.

Erweiterungsmethoden werden bereitgestellt, um die Verwendung von Polly-Richtlinien mit konfigurierten HttpClient-Instanzen zu aktivieren.Extension methods are provided to enable the use of Polly policies with configured HttpClient instances. Die Polly-Erweiterungen:The Polly extensions:

  • Unterstützen das Hinzufügen von Polly-basierten Handlern zu Clients.Support adding Polly-based handlers to clients.
  • Können nach Installation des Microsoft.Extensions.Http.Polly-NuGet-Pakets verwendet werden.Can be used after installing the Microsoft.Extensions.Http.Polly NuGet package. Das Paket ist nicht im freigegebenen ASP.NET Core-Framework enthalten.The package isn't included in the ASP.NET Core shared framework.

Behandeln von vorrübergehenden FehlernHandle transient faults

Am häufigsten treten Fehler auf, wenn externe HTTP-Aufrufe vorübergehend sind.Most common faults occur when external HTTP calls are transient. Eine praktische Erweiterungsmethode namens AddTransientHttpErrorPolicy ist enthalten. Sie ermöglicht das Definieren von Richtlinien zum Behandeln vorübergehender Fehler.A convenient extension method called AddTransientHttpErrorPolicy is included which allows a policy to be defined to handle transient errors. Richtlinien, die mit dieser Erweiterungsmethode konfiguriert wurden, behandeln HttpRequestException, HTTP 5xx-Antworten und HTTP 408-Antworten.Policies configured with this extension method handle HttpRequestException, HTTP 5xx responses, and HTTP 408 responses.

Die AddTransientHttpErrorPolicy-Erweiterung kann in Startup.ConfigureServices verwendet werden.The AddTransientHttpErrorPolicy extension can be used within Startup.ConfigureServices. Die Erweiterung ermöglicht den Zugriff auf ein PolicyBuilder-Objekt, das für die Fehlerbehandlung konfiguriert wurde und mögliche vorübergehende Fehler darstellt:The extension provides access to a PolicyBuilder object configured to handle errors representing a possible transient fault:

services.AddHttpClient<UnreliableEndpointCallerService>()
    .AddTransientHttpErrorPolicy(p => 
        p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));

Im vorangehenden Code wird eine WaitAndRetryAsync-Richtlinie definiert.In the preceding code, a WaitAndRetryAsync policy is defined. Anforderungsfehler werden bis zu dreimal mit einer Verzögerung von 600 ms wiederholt.Failed requests are retried up to three times with a delay of 600 ms between attempts.

Dynamisches Auswählen von RichtlinienDynamically select policies

Es gibt zusätzliche Erweiterungsmethoden, die zum Hinzufügen von Polly-basierten Handlern verwendet werden können.Additional extension methods exist which can be used to add Polly-based handlers. Eine dieser Erweiterungen ist AddPolicyHandler, die über mehrere Überladungen verfügt.One such extension is AddPolicyHandler, which has multiple overloads. Eine Überladung ermöglicht, dass die Anforderung beim Definieren der zu verwendenden Richtlinie überprüft werden kann:One overload allows the request to be inspected when defining which policy to apply:

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.In the preceding code, if the outgoing request is an HTTP GET, a 10-second timeout is applied. Für alle anderen HTTP-Methoden wird ein 30 Sekunden langer Timeout verwendet.For any other HTTP method, a 30-second timeout is used.

Hinzufügen mehrerer Polly-HandlerAdd multiple Polly handlers

Es ist üblich, Polly-Richtlinien zu schachteln, um erweiterte Funktionen bereitzustellen:It's common to nest Polly policies to provide enhanced functionality:

services.AddHttpClient("multiplepolicies")
    .AddTransientHttpErrorPolicy(p => p.RetryAsync(3))
    .AddTransientHttpErrorPolicy(
        p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));

Im vorangehenden Beispiel werden zwei Handler hinzugefügt.In the preceding example, two handlers are added. Der Erste verwendet die AddTransientHttpErrorPolicy-Erweiterung, um eine Wiederholungsrichtlinie hinzuzufügen.The first uses the AddTransientHttpErrorPolicy extension to add a retry policy. Fehlgeschlagene Anforderungen werden bis zu drei Mal wiederholt.Failed requests are retried up to three times. Der zweite Aufruf von AddTransientHttpErrorPolicy fügt eine Trennschalterrichtlinie hinzu.The second call to AddTransientHttpErrorPolicy adds a circuit breaker policy. Weitere externe Anforderungen werden 30 Sekunden lang blockiert, wenn fünf Fehlversuche hintereinander stattfinden.Further external requests are blocked for 30 seconds if five failed attempts occur sequentially. Trennschalterrichtlinien sind zustandsbehaftet.Circuit breaker policies are stateful. Alle Aufrufe über diesen Client teilen den gleichen Schalterzustand.All calls through this client share the same circuit state.

Hinzufügen von Richtlinien aus der Polly-RegistrierungAdd policies from the Polly registry

Eine Methode zum Verwalten regelmäßig genutzter Richtlinien ist, Sie einmal zu definieren und mit PolicyRegistry zu registrieren.An approach to managing regularly used policies is to define them once and register them with a PolicyRegistry. Eine Erweiterungsmethode wird bereitgestellt, die einem Handler ermöglicht, mithilfe einer Richtlinie aus der Registrierung hinzugefügt zu werden:An extension method is provided which allows a handler to be added using a policy from the registry:

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.In the preceding code, two policies are registered when the PolicyRegistry is added to the ServiceCollection. Damit eine Richtlinie aus der Registrierung verwendet werden kann, wird die Methode AddPolicyHandlerFromRegistry verwendet. Dabei wird der Name der anzuwendenden Richtlinie übergeben.To use a policy from the registry, the AddPolicyHandlerFromRegistry method is used, passing the name of the policy to apply.

Weitere Informationen zu IHttpClientFactory und Polly-Integrationen finden Sie im Polly Wiki.Further information about IHttpClientFactory and Polly integrations can be found on the Polly wiki.

HttpClient und die Verwaltung der LebensdauerHttpClient and lifetime management

Bei jedem Aufruf von CreateClient in der IHttpClientFactory wird eine neue Instanz von HttpClient zurückgegeben.A new HttpClient instance is returned each time CreateClient is called on the IHttpClientFactory. Es gibt einen HttpMessageHandler pro benanntem Client.There's an HttpMessageHandler per named client. Die Factory verwaltet die Lebensdauer der HttpMessageHandler-Instanzen.The factory manages the lifetimes of the HttpMessageHandler instances.

IHttpClientFactory legt die HttpMessageHandler-Instanzen zusammen, die von der Factory zum Reduzieren des Ressourcenverbrauchs erstellt wurden.IHttpClientFactory pools the HttpMessageHandler instances created by the factory to reduce resource consumption. Eine HttpMessageHandler-Instanz kann aus dem Pool wiederverwendet werden, wenn eine neue HttpClient-Instanz erstellt wird und deren Lebensdauer noch nicht abgelaufen ist.An HttpMessageHandler instance may be reused from the pool when creating a new HttpClient instance if its lifetime hasn't expired.

Das Zusammenlegen von Handlern ist ein wünschenswerter Vorgang, da jeder Handler in der Regel seine zugrunde liegenden HTTP-Verbindungen selbst verwaltet.Pooling of handlers is desirable as each handler typically manages its own underlying HTTP connections. Wenn mehr Handler als nötig erstellt werden, können Verzögerungen bei Verbindungen entstehen.Creating more handlers than necessary can result in connection delays. Einige Handler halten Verbindungen auch unbegrenzt offen, was verhindert, dass der Handler auf DNS-Änderungen reagiert.Some handlers also keep connections open indefinitely, which can prevent the handler from reacting to DNS changes.

Die Standardlebensdauer von Handlern beträgt zwei Minuten.The default handler lifetime is two minutes. Der Standardwert kann für jeden benannten Client überschrieben werden.The default value can be overridden on a per named client basis. Rufen Sie SetHandlerLifetime auf dem bei der Erstellung des Clients zurückgegebenen IHttpClientBuilder auf, um den Wert zu überschreiben:To override it, call SetHandlerLifetime on the IHttpClientBuilder that is returned when creating the client:

services.AddHttpClient("extendedhandlerlifetime")
    .SetHandlerLifetime(TimeSpan.FromMinutes(5));

Das Verwerfen des Client ist nicht erforderlich.Disposal of the client isn't required. 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.Disposal cancels outgoing requests and guarantees the given HttpClient instance can't be used after calling Dispose. IHttpClientFactory verfolgt von HttpClient-Instanzen verwendete Ressourcen nach und verwirft sie.IHttpClientFactory tracks and disposes resources used by HttpClient instances. Die HttpClient-Instanzen können im Allgemeinen als .NET-Objekte behandelt werden, die nicht verworfen werden müssen.The HttpClient instances can generally be treated as .NET objects not requiring disposal.

Das Beibehalten einer einzelnen HttpClient-Instanz für einen langen Zeitraum ist ein allgemeines Muster, das vor der Einführung von IHttpClientFactory verwendet wurde.Keeping a single HttpClient instance alive for a long duration is a common pattern used before the inception of IHttpClientFactory. Dieses Muster wird nach der Migration zu IHttpClientFactory überflüssig.This pattern becomes unnecessary after migrating to IHttpClientFactory.

Alternativen zu IHttpClientFactoryAlternatives to IHttpClientFactory

Mit der Verwendung von IHttpClientFactory in einer DI-fähigen App wird Folgendes vermieden:Using IHttpClientFactory in a DI-enabled app avoids:

  • Probleme mit der Ressourcenauslastung durch Zusammenlegen von HttpMessageHandler-InstanzenResource exhaustion problems by pooling HttpMessageHandler instances.
  • Probleme durch veraltetes DNS durch regelmäßigen Wechsel von HttpMessageHandler-InstanzenStale DNS problems by cycling HttpMessageHandler instances at regular intervals.

Es gibt alternative Möglichkeiten zum Lösen des vorangehenden Problems mithilfe einer langlebigen SocketsHttpHandler-Instanz:There are alternative ways to solve the preceding problems using a long-lived SocketsHttpHandler instance.

  • Erstellen Sie eine SocketsHttpHandler-Instanz, wenn die App gestartet wird, und verwenden Sie diese für die Lebensdauer der App.Create an instance of SocketsHttpHandler when the app starts and use it for the life of the app.
  • Konfigurieren Sie einen geeigneten Wert für PooledConnectionLifetime basierend auf den DNS-Aktualisierungszeiten.Configure PooledConnectionLifetime to an appropriate value based on DNS refresh times.
  • Erstellen Sie bei Bedarf HttpClient-Instanzen mithilfe von new HttpClient(handler, disposeHandler: false).Create HttpClient instances using new HttpClient(handler, disposeHandler: false) as needed.

Diese Ansätze lösen die Ressourcenverwaltungsprobleme, die IHttpClientFactory auf ähnliche Weise löst.The preceding approaches solve the resource management problems that IHttpClientFactory solves in a similar way.

  • SocketsHttpHandler gibt Verbindungen für HttpClient-Instanzen frei.The SocketsHttpHandler shares connections across HttpClient instances. Durch diese Freigabe wird die Erschöpfung von Sockets vermieden.This sharing prevents socket exhaustion.
  • SocketsHttpHandler wechselt die Verbindungen anhand von PooledConnectionLifetime, um Probleme durch veraltetes DNS zu umgehen.The SocketsHttpHandler cycles connections according to PooledConnectionLifetime to avoid stale DNS problems.

CookiesCookies

Die zusammengelegten HttpMessageHandler-Instanzen resultieren in der Freigabe von CookieContainer-Objekten.The pooled HttpMessageHandler instances results in CookieContainer objects being shared. Die unerwartete Freigabe von CookieContainer-Objekten führt oft zu fehlerhaftem Code.Unanticipated CookieContainer object sharing often results in incorrect code. Ziehen Sie für Apps, die Cookies erfordern, folgende Vorgehensweisen in Betracht:For apps that require cookies, consider either:

  • Deaktivieren der automatischen CookieverarbeitungDisabling automatic cookie handling
  • Vermeiden der Verwendung von IHttpClientFactoryAvoiding IHttpClientFactory

Rufen Sie ConfigurePrimaryHttpMessageHandler auf, um die automatische Cookieverarbeitung zu deaktivieren:Call ConfigurePrimaryHttpMessageHandler to disable automatic cookie handling:

services.AddHttpClient("configured-disable-automatic-cookies")
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        return new SocketsHttpHandler()
        {
            UseCookies = false,
        };
    });

ProtokollierungLogging

Über IHttpClientFactory erstellte Clients zeichnen Protokollmeldungen für alle Anforderungen auf.Clients created via IHttpClientFactory record log messages for all requests. Aktivieren Sie die entsprechende Informationsebene in Ihrer Protokollierungskonfiguration, um die Standardprotokollmeldungen anzuzeigen.Enable the appropriate information level in your logging configuration to see the default log messages. Zusätzliche Protokollierung, z.B. das Protokollieren von Anforderungsheadern, wird nur auf der Ablaufverfolgungsebene enthalten.Additional logging, such as the logging of request headers, is only included at trace level.

Die Protokollierungskategorie für jeden Client enthält den Namen des Clients.The log category used for each client includes the name of the client. Ein Client namens MyNamedClient protokolliert beispielsweise Meldungen mit der Kategorie System.Net.Http.HttpClient.MyNamedClient.LogicalHandler.A client named MyNamedClient, for example, logs messages with a category of System.Net.Http.HttpClient.MyNamedClient.LogicalHandler. Meldungen mit dem Suffix LogicalHandler treten außerhalb der Anforderungshandlerpipeline auf.Messages suffixed with LogicalHandler occur outside the request handler pipeline. In der Anforderung werden Meldungen protokolliert, bevor andere Handler in der Pipeline sie verarbeitet haben.On the request, messages are logged before any other handlers in the pipeline have processed it. In der Antwort werden Meldungen protokolliert, nachdem andere Handler in der Pipeline die Antwort empfangen haben.On the response, messages are logged after any other pipeline handlers have received the response.

Die Protokollierung tritt ebenfalls innerhalb der Anforderungshandlerpipeline auf.Logging also occurs inside the request handler pipeline. Im Beispiel MyNamedClient werden diese Meldungen für die Protokollkategorie System.Net.Http.HttpClient.MyNamedClient.ClientHandler protokolliert.In the MyNamedClient example, those messages are logged against the log category System.Net.Http.HttpClient.MyNamedClient.ClientHandler. Dies findet für die Anforderung statt, nachdem alle anderen Handler ausgeführt wurden und bevor die Anforderung an das Netzwerk gesendet wird.For the request, this occurs after all other handlers have run and immediately before the request is sent out on the network. Diese Protokollierung enthält den Zustand der Antwort in der Antwort, bevor sie an die Handlerpipeline zurückgegeben wird.On the response, this logging includes the state of the response before it passes back through the handler pipeline.

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.Enabling logging outside and inside the pipeline enables inspection of the changes made by the other pipeline handlers. Dies kann beispielsweise die Änderungen an Anforderungsheadern oder am Statuscode der Antwort enthalten.This may include changes to request headers, for example, or to the response status code.

Das Einschließen des Namens des Clients in der Protokollkategorie aktiviert bei Bedarf die Protokollfilterung für spezifische benannte Clients.Including the name of the client in the log category enables log filtering for specific named clients where necessary.

Konfigurieren von HttpMessageHandlerConfigure the HttpMessageHandler

Es kann notwendig sein, die Konfiguration des inneren von einem Client verwendeten HttpMessageHandler zu steuern.It may be necessary to control the configuration of the inner HttpMessageHandler used by a client.

IHttpClientBuilder wird zurückgegeben, wenn benannte oder typisierte Clients hinzugefügt werden.An IHttpClientBuilder is returned when adding named or typed clients. Die Erweiterungsmethode ConfigurePrimaryHttpMessageHandler kann zum Definieren eines Delegaten verwendet werden.The ConfigurePrimaryHttpMessageHandler extension method can be used to define a delegate. Der Delegat wird verwendet, um den primären HttpMessageHandler zu erstellen und konfigurieren, der von dem Client verwendet wird:The delegate is used to create and configure the primary HttpMessageHandler used by that client:

services.AddHttpClient("configured-inner-handler")
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        return new HttpClientHandler()
        {
            AllowAutoRedirect = false,
            UseDefaultCredentials = true
        };
    });

Verwenden von IHttpClientFactory in einer Konsolen-AppUse IHttpClientFactory in a console app

Fügen Sie in einer Konsolen-App dem Projekt die folgenden Paketverweise hinzu:In a console app, add the following package references to the project:

Im folgenden Beispiel:In the following example:

  • IHttpClientFactory ist im Dienstcontainer des generischen Hosts registriert.IHttpClientFactory is registered in the Generic Host's service container.
  • MyService erstellt eine Clientfactoryinstanz aus dem-Dienst, der zum Erstellen eines HttpClientverwendet wird.MyService creates a client factory instance from the service, which is used to create an HttpClient. HttpClient wird zum Abrufen einer Webseite verwendet.HttpClient is used to retrieve a webpage.
  • Main erstellt einen Bereich, um die GetPage-Methode des Diensts auszuführen und die ersten 500 Zeichen des Webseiteninhalts in die Konsole zu schreiben.Main creates a scope to execute the service's GetPage method and write the first 500 characters of the webpage content to the console.
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();

        using (var serviceScope = host.Services.CreateScope())
        {
            var services = serviceScope.ServiceProvider;

            try
            {
                var myService = services.GetRequiredService<IMyService>();
                var pageContent = await myService.GetPage();

                Console.WriteLine(pageContent.Substring(0, 500));
            }
            catch (Exception ex)
            {
                var logger = 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}";
            }
        }
    }
}

Zusätzliche RessourcenAdditional resources

Von Glenn Condron, Ryan Nowak, und Steve GordonBy Glenn Condron, Ryan Nowak, and Steve Gordon

IHttpClientFactory kann registriert und zum Konfigurieren und Erstellen von HttpClient-Instanzen in einer App verwendet werden.An IHttpClientFactory can be registered and used to configure and create HttpClient instances in an app. Dies hat folgende Vorteile:It offers the following benefits:

  • Ein zentraler Ort für das Benennen und Konfigurieren logischer HttpClient-Instanzen wird damit geboten.Provides a central location for naming and configuring logical HttpClient instances. Ein GitHub-Client kann beispielsweise registriert werden und so konfiguriert werden, um auf GitHub zuzugreifen.For example, a github client can be registered and configured to access GitHub. Ein Standard-Client kann für andere Zwecke registriert werden.A default client can be registered for other purposes.
  • Das Konzept der ausgehenden Middleware wird über delegierende Handler in HttpClient in Code umgesetzt. Außerdem werden Erweiterungen für auf Polly basierende Middleware bereitgestellt, die dies nutzen.Codifies the concept of outgoing middleware via delegating handlers in HttpClient and provides extensions for Polly-based middleware to take advantage of that.
  • Das Pooling und die Lebensdauer von zugrunde liegenden HttpClientMessageHandler-Instanzen werden verwaltet, um gängige DNS-Probleme zu vermeiden, die bei der manuellen Verwaltung der HttpClient-Lebensdauer auftreten.Manages the pooling and lifetime of underlying HttpClientMessageHandler instances to avoid common DNS problems that occur when manually managing HttpClient lifetimes.
  • Eine konfigurierbare Protokollierungsfunktion wird (über ILogger) für alle Anforderungen hinzugefügt, die über Clients gesendet werden, die von der Factory erstellt wurden.Adds a configurable logging experience (via ILogger) for all requests sent through clients created by the factory.

Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)View or download sample code (how to download)

VoraussetzungenPrerequisites

Für Projekte mit der Zielplattform .NET Framework muss das NuGet-Paket Microsoft.Extensions.Http installiert werden.Projects targeting .NET Framework require installation of the Microsoft.Extensions.Http NuGet package. Projekte für .NET Core, die auf das Metapaket Microsoft.AspNetCore.App verweisen, enthalten bereits das Microsoft.Extensions.Http-Paket.Projects that target .NET Core and reference the Microsoft.AspNetCore.App metapackage already include the Microsoft.Extensions.Http package.

VerbrauchsmusterConsumption patterns

Es gibt mehrere Möglichkeiten IHttpClientFactory in einer App zu verwenden:There are several ways IHttpClientFactory can be used in an app:

Keine dieser Möglichkeiten ist einer anderen überlegen.None of them are strictly superior to another. Der beste Ansatz richtet sich nach den Einschränkungen der App.The best approach depends upon the app's constraints.

Grundlegende VerwendungBasic usage

IHttpClientFactory kann durch Aufrufen der Erweiterungsmethode AddHttpClient auf IServiceCollection in der Methode Startup.ConfigureServices registriert werden.The IHttpClientFactory can be registered by calling the AddHttpClient extension method on the IServiceCollection, inside the Startup.ConfigureServices method.

services.AddHttpClient();

Nach der Registrierung kann Code IHttpClientFactory überall akzeptieren, wo Dienste mithilfe der Dependency Injection (DI) eingefügt werden können.Once registered, code can accept an IHttpClientFactory anywhere services can be injected with dependency injection (DI). IHttpClientFactory kann zum Erstellen einer HttpClient-Instanz verwendet werden:The IHttpClientFactory can be used to create an HttpClient instance:

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/aspnet/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.Using IHttpClientFactory in this fashion is a good way to refactor an existing app. Dies hat keine Auswirkung auf die Verwendung von HttpClient.It has no impact on the way HttpClient is used. An Stellen, in denen HttpClient-Instanzen derzeit erstellt werden, können Sie diese Ereignisse mit einem Aufruf von CreateClient ersetzen.In places where HttpClient instances are currently created, replace those occurrences with a call to CreateClient.

Benannte ClientsNamed clients

Wenn eine App mehrere verschiedene Verwendungen von HttpClient mit jeweils unterschiedlicher Konfiguration erfordert, ist die Verwendung von benannten Clients eine Option.If an app requires many distinct uses of HttpClient, each with a different configuration, an option is to use named clients. Die Konfiguration einer benannten HttpClient kann während der Registrierung in Startup.ConfigureServices angegeben werden.Configuration for a named HttpClient can be specified during registration in Startup.ConfigureServices.

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.In the preceding code, AddHttpClient is called, providing the name github. Für diesen Client wird Standardkonfiguration angewendet, d.h. die Basisadresse und zwei Header, die erforderlich sind, um mit der GitHub-API zu funktionieren.This client has some default configuration applied—namely the base address and two headers required to work with the GitHub API.

Wenn CreateClient aufgerufen wird, wird jedes Mal eine neue Instanz von HttpClient erstellt und die Konfigurationsaktion aufgerufen.Each time CreateClient is called, a new instance of HttpClient is created and the configuration action is called.

Zum Verarbeiten eines benannten Clients kann ein Zeichenfolgenparameter an CreateClient übergeben werden.To consume a named client, a string parameter can be passed to CreateClient. Geben Sie den Namen des zu erstellenden Clients an:Specify the name of the client to be created:

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/aspnet/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.In the preceding code, the request doesn't need to specify a hostname. Sie muss nur den Pfad übergeben, da die für den Client konfigurierte Basisadresse verwendet wird.It can pass just the path, since the base address configured for the client is used.

Typisierte ClientsTyped clients

Typisierte Clients:Typed clients:

  • Stellen dieselben Funktionen wie benannte Clients bereit, ohne Zeichenfolgen als Schlüssel verwenden zu müssen.Provide the same capabilities as named clients without the need to use strings as keys.
  • Bieten Hilfe durch IntelliSense und Compiler beim Verarbeiten von Clients.Provides IntelliSense and compiler help when consuming clients.
  • Stellen einen einzelnen Ort zum Konfigurieren und Interagieren mit einem bestimmten HttpClient bereit.Provide a single location to configure and interact with a particular HttpClient. 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.For example, a single typed client might be used for a single backend endpoint and encapsulate all logic dealing with that endpoint.
  • Funktionieren mit DI und können überall in Ihrer App eingefügt werden, wo sie benötigt werden.Work with DI and can be injected where required in your app.

Ein typisierter Client akzeptiert einen HttpClient-Parameter in seinem Konstruktor:A typed client accepts an HttpClient parameter in its constructor:

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/aspnet/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.In the preceding code, the configuration is moved into the typed client. Das HttpClient-Objekt wird als öffentliche Eigenschaft zur Verfügung gestellt.The HttpClient object is exposed as a public property. Es ist möglich, API-spezifische Methoden zu definieren, die die HttpClient-Funktionalität zur Verfügung stellen.It's possible to define API-specific methods that expose HttpClient functionality. Die Methode GetAspNetDocsIssues kapselt den Code, der für die Abfrage und Analyse des neuesten offenen Problems aus dem GitHub-Repository erforderlich ist.The GetAspNetDocsIssues method encapsulates the code needed to query for and parse out the latest open issues from a GitHub repository.

Zum Registrieren eines typisierten Clients kann die generische Erweiterungsmethode AddHttpClient innerhalb von Startup.ConfigureServices verwendet werden, die die typisierte Klasse angeben:To register a typed client, the generic AddHttpClient extension method can be used within Startup.ConfigureServices, specifying the typed client class:

services.AddHttpClient<GitHubService>();

Der typisierte Client wird mit DI als „vorübergehend“ registriert.The typed client is registered as transient with DI. Der typisierte Client kann direkt eingefügt und verwendet werden:The typed client can be injected and consumed directly:

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:If preferred, the configuration for a typed client can be specified during registration in Startup.ConfigureServices, rather than in the typed client's constructor:

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.It's possible to entirely encapsulate the HttpClient within a typed client. Anstatt ihn als eine Eigenschaft zur Verfügung zu stellen, können öffentliche Methoden bereitgestellt werden, die die HttpClient-Instanz intern aufrufen.Rather than exposing it as a property, public methods can be provided which call the HttpClient instance internally.

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.In the preceding code, the HttpClient is stored as a private field. Alle Zugriffe, die externe Aufrufe durchführen, durchlaufen die Methode GetRepos.All access to make external calls goes through the GetRepos method.

Generierte ClientsGenerated clients

IHttpClientFactory kann in Verbindung mit anderen Bibliotheken von Drittanbietern verwendet werden, z.B. Refit.IHttpClientFactory can be used in combination with other third-party libraries such as Refit. Refit ist eine REST-Bibliothek für .NET.Refit is a REST library for .NET. Sie konvertiert REST-APIs in Live-Schnittstellen.It converts REST APIs into live interfaces. Eine Implementierung der Schnittstelle wird dynamisch von RestService generiert. HttpClient wird für die externen HTTP-Aufrufe verwendet.An implementation of the interface is generated dynamically by the RestService, using HttpClient to make the external HTTP calls.

Eine Schnittstelle und eine Antwort werden definiert, um die externe API und die Antwort darzustellen:An interface and a reply are defined to represent the external API and its response:

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:A typed client can be added, using Refit to generate the implementation:

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:The defined interface can be consumed where necessary, with the implementation provided by DI and Refit:

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

Middleware für ausgehende AnforderungenOutgoing request middleware

HttpClient enthält bereits das Konzept, Handler zu delegieren, die für ausgehende HTTP-Anforderungen miteinander verknüpft werden können.HttpClient already has the concept of delegating handlers that can be linked together for outgoing HTTP requests. IHttpClientFactory erleichtert das Definieren der Handler, die für jeden benannten Client angewendet werden.The IHttpClientFactory makes it easy to define the handlers to apply for each named client. Die Registrierung und Verkettung von mehreren Handlern wird unterstützt, um eine Pipeline für die Middleware für ausgehende Anforderungen zu erstellen.It supports registration and chaining of multiple handlers to build an outgoing request middleware pipeline. Jeder dieser Handler kann vor und nach der ausgehenden Anforderung Aufgaben ausführen.Each of these handlers is able to perform work before and after the outgoing request. Dieses Muster ähnelt der eingehenden Pipeline für Middleware in ASP.NET Core.This pattern is similar to the inbound middleware pipeline 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.The pattern provides a mechanism to manage cross-cutting concerns around HTTP requests, including caching, error handling, serialization, and logging.

Definieren Sie zum Erstellen eines Handlers eine von DelegatingHandler abgeleitete Klasse.To create a handler, define a class deriving from DelegatingHandler. Überschreiben Sie die Methode SendAsync, um Code auszuführen, bevor die Anforderung an den nächsten Handler in der Pipeline übergeben wird:Override the SendAsync method to execute code before passing the request to the next handler in the pipeline:

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.The preceding code defines a basic handler. Er überprüft, ob ein X-API-KEY-Header in die Anforderung eingefügt wurde.It checks to see if an X-API-KEY header has been included on the request. Wenn der Header fehlt, kann er den HTTP-Aufruf vermeiden und eine geeignete Antwort zurückgeben.If the header is missing, it can avoid the HTTP call and return a suitable response.

Während der Registrierung kann mindestens ein Handler der Konfiguration eines HttpClient hinzugefügt werden.During registration, one or more handlers can be added to the configuration for an HttpClient. Dieser Vorgang wird über die Erweiterungsmethoden von IHttpClientBuilder ermöglicht.This task is accomplished via extension methods on the IHttpClientBuilder.

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.In the preceding code, the ValidateHeaderHandler is registered with DI. Der Handler muss in DI als vorübergehender Dienst registriert sein, niemals bereichsbezogen.The handler must be registered in DI as a transient service, never scoped. Wenn der Handler als bereichsbezogener Dienst registriert ist und alle Dienste, von denen der Handler abhängig ist, gelöscht werden können:If the handler is registered as a scoped service and any services that the handler depends upon are disposable:

  • Die Dienste des Handlers können verworfen werden, bevor der Handler den Gültigkeitsbereich verlässt.The handler's services could be disposed before the handler goes out of scope.
  • Der verworfenen Handlerdienst bewirken, dass der Handler fehlschlägt.The disposed handler services causes the handler to fail.

Nach der Registrierung kann AddHttpMessageHandler aufgerufen werden, wobei der Typ für den Handler übergeben wird.Once registered, AddHttpMessageHandler can be called, passing in the handler type.

Mehrere Handler können in der Reihenfolge registriert werden, in der sie ausgeführt werden sollen.Multiple handlers can be registered in the order that they should execute. Jeder Handler umschließt den nächsten Handler, bis der endgültige HttpClientHandler die Anforderung ausführt:Each handler wraps the next handler until the final HttpClientHandler executes the request:

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:Use one of the following approaches to share per-request state with message handlers:

  • Übergeben Sie Daten mit HttpRequestMessage.Properties an den Handler.Pass data into the handler using HttpRequestMessage.Properties.
  • Greifen Sie mit IHttpContextAccessor auf die aktuelle Anforderung zu.Use IHttpContextAccessor to access the current request.
  • Erstellen Sie ein benutzerdefiniertes AsyncLocal-Speicherobjekt, um die Daten zu übergeben.Create a custom AsyncLocal storage object to pass the data.

Verwenden von Polly-basierten HandlernUse Polly-based handlers

IHttpClientFactory integriert mit der beliebten Drittanbieter-Bibliothek namens Polly.IHttpClientFactory integrates with a popular third-party library called Polly. Polly ist eine umfassende Bibliothek für die Behandlung von beständigen und vorübergehenden Fehlern für .NET.Polly is a comprehensive resilience and transient fault-handling library for .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.It allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.

Erweiterungsmethoden werden bereitgestellt, um die Verwendung von Polly-Richtlinien mit konfigurierten HttpClient-Instanzen zu aktivieren.Extension methods are provided to enable the use of Polly policies with configured HttpClient instances. Die Polly-Erweiterungen:The Polly extensions:

  • Unterstützen das Hinzufügen von Polly-basierten Handlern zu Clients.Support adding Polly-based handlers to clients.
  • Können nach Installation des Microsoft.Extensions.Http.Polly-NuGet-Pakets verwendet werden.Can be used after installing the Microsoft.Extensions.Http.Polly NuGet package. Das Paket ist nicht im freigegebenen ASP.NET Core-Framework enthalten.The package isn't included in the ASP.NET Core shared framework.

Behandeln von vorrübergehenden FehlernHandle transient faults

Am häufigsten treten Fehler auf, wenn externe HTTP-Aufrufe vorübergehend sind.Most common faults occur when external HTTP calls are transient. Eine praktische Erweiterungsmethode namens AddTransientHttpErrorPolicy ist enthalten. Sie ermöglicht das Definieren von Richtlinien zum Behandeln vorübergehender Fehler.A convenient extension method called AddTransientHttpErrorPolicy is included which allows a policy to be defined to handle transient errors. Richtlinien, die mit dieser Erweiterungsmethode konfiguriert wurden, behandeln HttpRequestException, HTTP 5xx-Antworten und HTTP 408-Antworten.Policies configured with this extension method handle HttpRequestException, HTTP 5xx responses, and HTTP 408 responses.

Die AddTransientHttpErrorPolicy-Erweiterung kann in Startup.ConfigureServices verwendet werden.The AddTransientHttpErrorPolicy extension can be used within Startup.ConfigureServices. Die Erweiterung ermöglicht den Zugriff auf ein PolicyBuilder-Objekt, das für die Fehlerbehandlung konfiguriert wurde und mögliche vorübergehende Fehler darstellt:The extension provides access to a PolicyBuilder object configured to handle errors representing a possible transient fault:

services.AddHttpClient<UnreliableEndpointCallerService>()
    .AddTransientHttpErrorPolicy(p => 
        p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));

Im vorangehenden Code wird eine WaitAndRetryAsync-Richtlinie definiert.In the preceding code, a WaitAndRetryAsync policy is defined. Anforderungsfehler werden bis zu dreimal mit einer Verzögerung von 600 ms wiederholt.Failed requests are retried up to three times with a delay of 600 ms between attempts.

Dynamisches Auswählen von RichtlinienDynamically select policies

Es gibt zusätzliche Erweiterungsmethoden, die zum Hinzufügen von Polly-basierten Handlern verwendet werden können.Additional extension methods exist which can be used to add Polly-based handlers. Eine dieser Erweiterungen ist AddPolicyHandler, die über mehrere Überladungen verfügt.One such extension is AddPolicyHandler, which has multiple overloads. Eine Überladung ermöglicht, dass die Anforderung beim Definieren der zu verwendenden Richtlinie überprüft werden kann:One overload allows the request to be inspected when defining which policy to apply:

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.In the preceding code, if the outgoing request is an HTTP GET, a 10-second timeout is applied. Für alle anderen HTTP-Methoden wird ein 30 Sekunden langer Timeout verwendet.For any other HTTP method, a 30-second timeout is used.

Hinzufügen mehrerer Polly-HandlerAdd multiple Polly handlers

Es ist üblich, Polly-Richtlinien zu schachteln, um erweiterte Funktionen bereitzustellen:It's common to nest Polly policies to provide enhanced functionality:

services.AddHttpClient("multiplepolicies")
    .AddTransientHttpErrorPolicy(p => p.RetryAsync(3))
    .AddTransientHttpErrorPolicy(
        p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));

Im vorangehenden Beispiel werden zwei Handler hinzugefügt.In the preceding example, two handlers are added. Der Erste verwendet die AddTransientHttpErrorPolicy-Erweiterung, um eine Wiederholungsrichtlinie hinzuzufügen.The first uses the AddTransientHttpErrorPolicy extension to add a retry policy. Fehlgeschlagene Anforderungen werden bis zu drei Mal wiederholt.Failed requests are retried up to three times. Der zweite Aufruf von AddTransientHttpErrorPolicy fügt eine Trennschalterrichtlinie hinzu.The second call to AddTransientHttpErrorPolicy adds a circuit breaker policy. Weitere externe Anforderungen werden 30 Sekunden lang blockiert, wenn fünf Fehlversuche hintereinander stattfinden.Further external requests are blocked for 30 seconds if five failed attempts occur sequentially. Trennschalterrichtlinien sind zustandsbehaftet.Circuit breaker policies are stateful. Alle Aufrufe über diesen Client teilen den gleichen Schalterzustand.All calls through this client share the same circuit state.

Hinzufügen von Richtlinien aus der Polly-RegistrierungAdd policies from the Polly registry

Eine Methode zum Verwalten regelmäßig genutzter Richtlinien ist, Sie einmal zu definieren und mit PolicyRegistry zu registrieren.An approach to managing regularly used policies is to define them once and register them with a PolicyRegistry. Eine Erweiterungsmethode wird bereitgestellt, die einem Handler ermöglicht, mithilfe einer Richtlinie aus der Registrierung hinzugefügt zu werden:An extension method is provided which allows a handler to be added using a policy from the registry:

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.In the preceding code, two policies are registered when the PolicyRegistry is added to the ServiceCollection. Damit eine Richtlinie aus der Registrierung verwendet werden kann, wird die Methode AddPolicyHandlerFromRegistry verwendet. Dabei wird der Name der anzuwendenden Richtlinie übergeben.To use a policy from the registry, the AddPolicyHandlerFromRegistry method is used, passing the name of the policy to apply.

Weitere Informationen zu IHttpClientFactory und Polly-Integrationen finden Sie im Polly Wiki.Further information about IHttpClientFactory and Polly integrations can be found on the Polly wiki.

HttpClient und die Verwaltung der LebensdauerHttpClient and lifetime management

Bei jedem Aufruf von CreateClient in der IHttpClientFactory wird eine neue Instanz von HttpClient zurückgegeben.A new HttpClient instance is returned each time CreateClient is called on the IHttpClientFactory. Es gibt einen HttpMessageHandler pro benanntem Client.There's an HttpMessageHandler per named client. Die Factory verwaltet die Lebensdauer der HttpMessageHandler-Instanzen.The factory manages the lifetimes of the HttpMessageHandler instances.

IHttpClientFactory legt die HttpMessageHandler-Instanzen zusammen, die von der Factory zum Reduzieren des Ressourcenverbrauchs erstellt wurden.IHttpClientFactory pools the HttpMessageHandler instances created by the factory to reduce resource consumption. Eine HttpMessageHandler-Instanz kann aus dem Pool wiederverwendet werden, wenn eine neue HttpClient-Instanz erstellt wird und deren Lebensdauer noch nicht abgelaufen ist.An HttpMessageHandler instance may be reused from the pool when creating a new HttpClient instance if its lifetime hasn't expired.

Das Zusammenlegen von Handlern ist ein wünschenswerter Vorgang, da jeder Handler in der Regel seine zugrunde liegenden HTTP-Verbindungen selbst verwaltet.Pooling of handlers is desirable as each handler typically manages its own underlying HTTP connections. Wenn mehr Handler als nötig erstellt werden, können Verzögerungen bei Verbindungen entstehen.Creating more handlers than necessary can result in connection delays. Einige Handler halten Verbindungen auch unbegrenzt offen, was verhindert, dass der Handler auf DNS-Änderungen reagiert.Some handlers also keep connections open indefinitely, which can prevent the handler from reacting to DNS changes.

Die Standardlebensdauer von Handlern beträgt zwei Minuten.The default handler lifetime is two minutes. Der Standardwert kann für jeden benannten Client überschrieben werden.The default value can be overridden on a per named client basis. Rufen Sie SetHandlerLifetime auf dem bei der Erstellung des Clients zurückgegebenen IHttpClientBuilder auf, um den Wert zu überschreiben:To override it, call SetHandlerLifetime on the IHttpClientBuilder that is returned when creating the client:

services.AddHttpClient("extendedhandlerlifetime")
    .SetHandlerLifetime(TimeSpan.FromMinutes(5));

Das Verwerfen des Client ist nicht erforderlich.Disposal of the client isn't required. 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.Disposal cancels outgoing requests and guarantees the given HttpClient instance can't be used after calling Dispose. IHttpClientFactory verfolgt von HttpClient-Instanzen verwendete Ressourcen nach und verwirft sie.IHttpClientFactory tracks and disposes resources used by HttpClient instances. Die HttpClient-Instanzen können im Allgemeinen als .NET-Objekte behandelt werden, die nicht verworfen werden müssen.The HttpClient instances can generally be treated as .NET objects not requiring disposal.

Das Beibehalten einer einzelnen HttpClient-Instanz für einen langen Zeitraum ist ein allgemeines Muster, das vor der Einführung von IHttpClientFactory verwendet wurde.Keeping a single HttpClient instance alive for a long duration is a common pattern used before the inception of IHttpClientFactory. Dieses Muster wird nach der Migration zu IHttpClientFactory überflüssig.This pattern becomes unnecessary after migrating to IHttpClientFactory.

Alternativen zu IHttpClientFactoryAlternatives to IHttpClientFactory

Mit der Verwendung von IHttpClientFactory in einer DI-fähigen App wird Folgendes vermieden:Using IHttpClientFactory in a DI-enabled app avoids:

  • Probleme mit der Ressourcenauslastung durch Zusammenlegen von HttpMessageHandler-InstanzenResource exhaustion problems by pooling HttpMessageHandler instances.
  • Probleme durch veraltetes DNS durch regelmäßigen Wechsel von HttpMessageHandler-InstanzenStale DNS problems by cycling HttpMessageHandler instances at regular intervals.

Es gibt alternative Möglichkeiten zum Lösen des vorangehenden Problems mithilfe einer langlebigen SocketsHttpHandler-Instanz:There are alternative ways to solve the preceding problems using a long-lived SocketsHttpHandler instance.

  • Erstellen Sie eine SocketsHttpHandler-Instanz, wenn die App gestartet wird, und verwenden Sie diese für die Lebensdauer der App.Create an instance of SocketsHttpHandler when the app starts and use it for the life of the app.
  • Konfigurieren Sie einen geeigneten Wert für PooledConnectionLifetime basierend auf den DNS-Aktualisierungszeiten.Configure PooledConnectionLifetime to an appropriate value based on DNS refresh times.
  • Erstellen Sie bei Bedarf HttpClient-Instanzen mithilfe von new HttpClient(handler, disposeHandler: false).Create HttpClient instances using new HttpClient(handler, disposeHandler: false) as needed.

Diese Ansätze lösen die Ressourcenverwaltungsprobleme, die IHttpClientFactory auf ähnliche Weise löst.The preceding approaches solve the resource management problems that IHttpClientFactory solves in a similar way.

  • SocketsHttpHandler gibt Verbindungen für HttpClient-Instanzen frei.The SocketsHttpHandler shares connections across HttpClient instances. Durch diese Freigabe wird die Erschöpfung von Sockets vermieden.This sharing prevents socket exhaustion.
  • SocketsHttpHandler wechselt die Verbindungen anhand von PooledConnectionLifetime, um Probleme durch veraltetes DNS zu umgehen.The SocketsHttpHandler cycles connections according to PooledConnectionLifetime to avoid stale DNS problems.

CookiesCookies

Die zusammengelegten HttpMessageHandler-Instanzen resultieren in der Freigabe von CookieContainer-Objekten.The pooled HttpMessageHandler instances results in CookieContainer objects being shared. Die unerwartete Freigabe von CookieContainer-Objekten führt oft zu fehlerhaftem Code.Unanticipated CookieContainer object sharing often results in incorrect code. Ziehen Sie für Apps, die Cookies erfordern, folgende Vorgehensweisen in Betracht:For apps that require cookies, consider either:

  • Deaktivieren der automatischen CookieverarbeitungDisabling automatic cookie handling
  • Vermeiden der Verwendung von IHttpClientFactoryAvoiding IHttpClientFactory

Rufen Sie ConfigurePrimaryHttpMessageHandler auf, um die automatische Cookieverarbeitung zu deaktivieren:Call ConfigurePrimaryHttpMessageHandler to disable automatic cookie handling:

services.AddHttpClient("configured-disable-automatic-cookies")
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        return new SocketsHttpHandler()
        {
            UseCookies = false,
        };
    });

ProtokollierungLogging

Über IHttpClientFactory erstellte Clients zeichnen Protokollmeldungen für alle Anforderungen auf.Clients created via IHttpClientFactory record log messages for all requests. Aktivieren Sie die entsprechende Informationsebene in Ihrer Protokollierungskonfiguration, um die Standardprotokollmeldungen anzuzeigen.Enable the appropriate information level in your logging configuration to see the default log messages. Zusätzliche Protokollierung, z.B. das Protokollieren von Anforderungsheadern, wird nur auf der Ablaufverfolgungsebene enthalten.Additional logging, such as the logging of request headers, is only included at trace level.

Die Protokollierungskategorie für jeden Client enthält den Namen des Clients.The log category used for each client includes the name of the client. Ein Client namens MyNamedClient protokolliert beispielsweise Meldungen mit der Kategorie System.Net.Http.HttpClient.MyNamedClient.LogicalHandler.A client named MyNamedClient, for example, logs messages with a category of System.Net.Http.HttpClient.MyNamedClient.LogicalHandler. Meldungen mit dem Suffix LogicalHandler treten außerhalb der Anforderungshandlerpipeline auf.Messages suffixed with LogicalHandler occur outside the request handler pipeline. In der Anforderung werden Meldungen protokolliert, bevor andere Handler in der Pipeline sie verarbeitet haben.On the request, messages are logged before any other handlers in the pipeline have processed it. In der Antwort werden Meldungen protokolliert, nachdem andere Handler in der Pipeline die Antwort empfangen haben.On the response, messages are logged after any other pipeline handlers have received the response.

Die Protokollierung tritt ebenfalls innerhalb der Anforderungshandlerpipeline auf.Logging also occurs inside the request handler pipeline. Im Beispiel MyNamedClient werden diese Meldungen für die Protokollkategorie System.Net.Http.HttpClient.MyNamedClient.ClientHandler protokolliert.In the MyNamedClient example, those messages are logged against the log category System.Net.Http.HttpClient.MyNamedClient.ClientHandler. Dies findet für die Anforderung statt, nachdem alle anderen Handler ausgeführt wurden und bevor die Anforderung an das Netzwerk gesendet wird.For the request, this occurs after all other handlers have run and immediately before the request is sent out on the network. Diese Protokollierung enthält den Zustand der Antwort in der Antwort, bevor sie an die Handlerpipeline zurückgegeben wird.On the response, this logging includes the state of the response before it passes back through the handler pipeline.

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.Enabling logging outside and inside the pipeline enables inspection of the changes made by the other pipeline handlers. Dies kann beispielsweise die Änderungen an Anforderungsheadern oder am Statuscode der Antwort enthalten.This may include changes to request headers, for example, or to the response status code.

Das Einschließen des Namens des Clients in der Protokollkategorie aktiviert bei Bedarf die Protokollfilterung für spezifische benannte Clients.Including the name of the client in the log category enables log filtering for specific named clients where necessary.

Konfigurieren von HttpMessageHandlerConfigure the HttpMessageHandler

Es kann notwendig sein, die Konfiguration des inneren von einem Client verwendeten HttpMessageHandler zu steuern.It may be necessary to control the configuration of the inner HttpMessageHandler used by a client.

IHttpClientBuilder wird zurückgegeben, wenn benannte oder typisierte Clients hinzugefügt werden.An IHttpClientBuilder is returned when adding named or typed clients. Die Erweiterungsmethode ConfigurePrimaryHttpMessageHandler kann zum Definieren eines Delegaten verwendet werden.The ConfigurePrimaryHttpMessageHandler extension method can be used to define a delegate. Der Delegat wird verwendet, um den primären HttpMessageHandler zu erstellen und konfigurieren, der von dem Client verwendet wird:The delegate is used to create and configure the primary HttpMessageHandler used by that client:

services.AddHttpClient("configured-inner-handler")
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        return new HttpClientHandler()
        {
            AllowAutoRedirect = false,
            UseDefaultCredentials = true
        };
    });

Verwenden von IHttpClientFactory in einer Konsolen-AppUse IHttpClientFactory in a console app

Fügen Sie in einer Konsolen-App dem Projekt die folgenden Paketverweise hinzu:In a console app, add the following package references to the project:

Im folgenden Beispiel:In the following example:

  • IHttpClientFactory ist im Dienstcontainer des generischen Hosts registriert.IHttpClientFactory is registered in the Generic Host's service container.
  • MyService erstellt eine Clientfactoryinstanz aus dem-Dienst, der zum Erstellen eines HttpClientverwendet wird.MyService creates a client factory instance from the service, which is used to create an HttpClient. HttpClient wird zum Abrufen einer Webseite verwendet.HttpClient is used to retrieve a webpage.
  • Main erstellt einen Bereich, um die GetPage-Methode des Diensts auszuführen und die ersten 500 Zeichen des Webseiteninhalts in die Konsole zu schreiben.Main creates a scope to execute the service's GetPage method and write the first 500 characters of the webpage content to the console.
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();

        using (var serviceScope = host.Services.CreateScope())
        {
            var services = serviceScope.ServiceProvider;

            try
            {
                var myService = services.GetRequiredService<IMyService>();
                var pageContent = await myService.GetPage();

                Console.WriteLine(pageContent.Substring(0, 500));
            }
            catch (Exception ex)
            {
                var logger = 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}";
            }
        }
    }
}

Middleware für HeaderweitergabeHeader propagation middleware

Für die Headerweitergabe wird eine von der Community unterstützte Middleware verwendet, um HTTP-Header von eingehenden Anforderungen an ausgehende HTTP-Clientanforderungen weiterzugeben.Header propagation is a community supported middleware to propagate HTTP headers from the incoming request to the outgoing HTTP Client requests. So verwenden Sie die Headerweitergabe:To use header propagation:

  • Erstellen Sie einen Verweis auf das von der Community unterstützte HeaderPropagation-Paket.Reference the community supported port of the package HeaderPropagation. ASp.NET Core 3.1 und höher unterstützen Microsoft.AspNetCore.HeaderPropagation.ASP.NET Core 3.1 and later supports Microsoft.AspNetCore.HeaderPropagation.

  • Konfigurieren Sie in Startup die Middleware und HttpClient:Configure the middleware and HttpClient in Startup:

    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:The client includes the configured headers on outbound requests:

    var client = clientFactory.CreateClient("MyForwardingClient");
    var response = client.GetAsync(...);
    

Zusätzliche RessourcenAdditional resources