Vytváření požadavků HTTP pomocí IHttpClientFactory v ASP.NET Core

Kirk Larkin, Steve Gordon, Glenn Condron a Ryan Nowak.

Můžete IHttpClientFactory ho zaregistrovat a použít ke konfiguraci a vytvoření HttpClient instancí v aplikaci. IHttpClientFactory nabízí následující výhody:

  • Poskytuje centrální umístění pro pojmenování a konfiguraci logických instancí HttpClient. Klient s názvem GitHub může být například zaregistrovaný a nakonfigurovaný pro přístup k GitHubu. Pro obecný přístup je možné zaregistrovat výchozího klienta.
  • Kodifikuje koncept odchozího middlewaru prostřednictvím delegování obslužných rutin v HttpClient. Poskytuje rozšíření pro middleware založený na Polly, které využívají delegování obslužných rutin v HttpClient.
  • Spravuje sdružování a životnost základních HttpClientMessageHandler instancí. Automatická správa zabraňuje běžným problémům s DNS (Domain Name System), ke kterým dochází při ruční správě HttpClient životnosti.
  • Přidá konfigurovatelné protokolování (prostřednictvím ILogger) pro všechny požadavky odeslané prostřednictvím klientů vytvořených továrnou.

Vzorový kód v této verzi tématu používá System.Text.Json k deserializaci JSobsahu ON vráceného v odpovědích HTTP. Pro ukázky, které používají Json.NET , a ReadAsAsync<T>pomocí selektoru verzí vyberte verzi 2.x tohoto tématu.

Spotřeby

V aplikaci můžete použít několik způsobů IHttpClientFactory :

Nejlepší přístup závisí na požadavcích aplikace.

Základní použití

Zaregistrujte IHttpClientFactory se volánímAddHttpClient:Program.cs

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddHttpClient();

O IHttpClientFactory injektáž závislostí (DI) je možné požádat. Následující kód používá IHttpClientFactory k vytvoření HttpClient instance:

public class BasicModel : PageModel
{
    private readonly IHttpClientFactory _httpClientFactory;

    public BasicModel(IHttpClientFactory httpClientFactory) =>
        _httpClientFactory = httpClientFactory;

    public IEnumerable<GitHubBranch>? GitHubBranches { get; set; }

    public async Task OnGet()
    {
        var httpRequestMessage = new HttpRequestMessage(
            HttpMethod.Get,
            "https://api.github.com/repos/dotnet/AspNetCore.Docs/branches")
        {
            Headers =
            {
                { HeaderNames.Accept, "application/vnd.github.v3+json" },
                { HeaderNames.UserAgent, "HttpRequestsSample" }
            }
        };

        var httpClient = _httpClientFactory.CreateClient();
        var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);

        if (httpResponseMessage.IsSuccessStatusCode)
        {
            using var contentStream =
                await httpResponseMessage.Content.ReadAsStreamAsync();
            
            GitHubBranches = await JsonSerializer.DeserializeAsync
                <IEnumerable<GitHubBranch>>(contentStream);
        }
    }
}

Použití IHttpClientFactory podobné jako v předchozím příkladu je dobrým způsobem, jak refaktorovat existující aplikaci. Nemá žádný vliv na způsob HttpClient použití. V místech, kde HttpClient se instance vytvářejí v existující aplikaci, nahraďte tyto výskyty voláními CreateClient.

Pojmenovaní klienti

Pojmenovaní klienti jsou dobrou volbou, když:

  • Aplikace vyžaduje mnoho různých použití HttpClient.
  • Mnoho HttpClientz nich má jinou konfiguraci.

Zadejte konfiguraci pro pojmenovanou HttpClient během registrace v Program.cs:

builder.Services.AddHttpClient("GitHub", httpClient =>
{
    httpClient.BaseAddress = new Uri("https://api.github.com/");

    // using Microsoft.Net.Http.Headers;
    // The GitHub API requires two headers.
    httpClient.DefaultRequestHeaders.Add(
        HeaderNames.Accept, "application/vnd.github.v3+json");
    httpClient.DefaultRequestHeaders.Add(
        HeaderNames.UserAgent, "HttpRequestsSample");
});

V předchozím kódu je klient nakonfigurovaný takto:

  • Základní adresa https://api.github.com/.
  • Dvě hlavičky potřebné pro práci s rozhraním API GitHubu

CreateClient

Pokaždé CreateClient se volá:

  • Vytvoří se nová instance HttpClient .
  • Volá se akce konfigurace.

Pokud chcete vytvořit pojmenovaného klienta, předejte jeho název do CreateClient:

public class NamedClientModel : PageModel
{
    private readonly IHttpClientFactory _httpClientFactory;

    public NamedClientModel(IHttpClientFactory httpClientFactory) =>
        _httpClientFactory = httpClientFactory;

    public IEnumerable<GitHubBranch>? GitHubBranches { get; set; }

    public async Task OnGet()
    {
        var httpClient = _httpClientFactory.CreateClient("GitHub");
        var httpResponseMessage = await httpClient.GetAsync(
            "repos/dotnet/AspNetCore.Docs/branches");

        if (httpResponseMessage.IsSuccessStatusCode)
        {
            using var contentStream =
                await httpResponseMessage.Content.ReadAsStreamAsync();
            
            GitHubBranches = await JsonSerializer.DeserializeAsync
                <IEnumerable<GitHubBranch>>(contentStream);
        }
    }
}

V předchozím kódu požadavek nemusí zadávat název hostitele. Kód může předat pouze cestu, protože se používá základní adresa nakonfigurovaná pro klienta.

Typoví klienti

Typoví klienti:

  • Poskytněte stejné možnosti jako pojmenovaní klienti bez nutnosti používat řetězce jako klíče.
  • Poskytuje nápovědu intelliSense a kompilátoru při využívání klientů.
  • Zadejte jedno umístění pro konfiguraci a interakci s určitým HttpClient. Může se například použít jeden typ klienta:
    • Pro jeden back-endový koncový bod.
    • Zapouzdření veškeré logiky, která se zabývá koncovým bodem.
  • Pracujte s DI a můžete je v aplikaci vsunou tam, kde je to potřeba.

Zadaný klient přijímá parametr v jeho konstruktoru HttpClient :

public class GitHubService
{
    private readonly HttpClient _httpClient;

    public GitHubService(HttpClient httpClient)
    {
        _httpClient = httpClient;

        _httpClient.BaseAddress = new Uri("https://api.github.com/");

        // using Microsoft.Net.Http.Headers;
        // The GitHub API requires two headers.
        _httpClient.DefaultRequestHeaders.Add(
            HeaderNames.Accept, "application/vnd.github.v3+json");
        _httpClient.DefaultRequestHeaders.Add(
            HeaderNames.UserAgent, "HttpRequestsSample");
    }

    public async Task<IEnumerable<GitHubBranch>?> GetAspNetCoreDocsBranchesAsync() =>
        await _httpClient.GetFromJsonAsync<IEnumerable<GitHubBranch>>(
            "repos/dotnet/AspNetCore.Docs/branches");
}

V předchozím kódu:

  • Konfigurace se přesune do zadaného klienta.
  • Zadanou HttpClient instanci se uloží jako soukromé pole.

Metody specifické pro rozhraní API je možné vytvořit, které zpřístupňují HttpClient funkce. GetAspNetCoreDocsBranches Například metoda zapouzdřuje kód pro načtení větví GitHubu na webu Docs.

Následující volání AddHttpClientProgram.cs kódu pro registraci GitHubService typové třídy klienta:

builder.Services.AddHttpClient<GitHubService>();

Zadaný klient je zaregistrovaný jako přechodný s DI. V předchozím kódu AddHttpClient se zaregistruje GitHubService jako přechodná služba. Tato registrace používá metodu továrny k:

  1. Vytvořte instanci HttpClient.
  2. Vytvoření instance , GitHubServicepředání instance HttpClient do jeho konstruktoru.

Zadaný klient se dá vloženého a spotřebovávat přímo:

public class TypedClientModel : PageModel
{
    private readonly GitHubService _gitHubService;

    public TypedClientModel(GitHubService gitHubService) =>
        _gitHubService = gitHubService;

    public IEnumerable<GitHubBranch>? GitHubBranches { get; set; }

    public async Task OnGet()
    {
        try
        {
            GitHubBranches = await _gitHubService.GetAspNetCoreDocsBranchesAsync();
        }
        catch (HttpRequestException)
        {
            // ...
        }
    }
}

Konfiguraci zadaného klienta lze také zadat během registrace v Program.csnástroji , nikoli v konstruktoru zadaného klienta:

builder.Services.AddHttpClient<GitHubService>(httpClient =>
{
    httpClient.BaseAddress = new Uri("https://api.github.com/");

    // ...
});

Vygenerované klienty

IHttpClientFactory lze použít v kombinaci s knihovnami třetích stran, jako je například Refit. Refit je REST knihovna pro .NET. REST Převádí rozhraní API na živá rozhraní. Volání AddRefitClient pro vygenerování dynamické implementace rozhraní, které používá HttpClient k provádění externích volání HTTP.

Vlastní rozhraní představuje externí rozhraní API:

public interface IGitHubClient
{
    [Get("/repos/dotnet/AspNetCore.Docs/branches")]
    Task<IEnumerable<GitHubBranch>> GetAspNetCoreDocsBranchesAsync();
}

Volání AddRefitClient pro vygenerování dynamické implementace a následné volání ConfigureHttpClient konfigurace podkladového HttpClientobjektu:

builder.Services.AddRefitClient<IGitHubClient>()
    .ConfigureHttpClient(httpClient =>
    {
        httpClient.BaseAddress = new Uri("https://api.github.com/");

        // using Microsoft.Net.Http.Headers;
        // The GitHub API requires two headers.
        httpClient.DefaultRequestHeaders.Add(
            HeaderNames.Accept, "application/vnd.github.v3+json");
        httpClient.DefaultRequestHeaders.Add(
            HeaderNames.UserAgent, "HttpRequestsSample");
    });

Použití DI pro přístup k dynamické implementaci IGitHubClient:

public class RefitModel : PageModel
{
    private readonly IGitHubClient _gitHubClient;

    public RefitModel(IGitHubClient gitHubClient) =>
        _gitHubClient = gitHubClient;

    public IEnumerable<GitHubBranch>? GitHubBranches { get; set; }

    public async Task OnGet()
    {
        try
        {
            GitHubBranches = await _gitHubClient.GetAspNetCoreDocsBranchesAsync();
        }
        catch (ApiException)
        {
            // ...
        }
    }
}

Vytváření požadavků POST, PUT a DELETE

V předchozích příkladech používají všechny požadavky HTTP příkaz GET HTTP. HttpClient také podporuje další příkazy HTTP, včetně:

  • POST
  • PUT
  • SMAZÁNÍ
  • PATCH

Úplný seznam podporovaných příkazů HTTP najdete v tématu HttpMethod.

Následující příklad ukazuje, jak vytvořit požadavek HTTP POST:

public async Task CreateItemAsync(TodoItem todoItem)
{
    var todoItemJson = new StringContent(
        JsonSerializer.Serialize(todoItem),
        Encoding.UTF8,
        Application.Json); // using static System.Net.Mime.MediaTypeNames;

    using var httpResponseMessage =
        await _httpClient.PostAsync("/api/TodoItems", todoItemJson);

    httpResponseMessage.EnsureSuccessStatusCode();
}

V předchozím kódu CreateItemAsync metoda:

  • Serializuje TodoItem parametr on JSon using System.Text.Json.
  • Vytvoří instanci StringContent pro zabalení serializované JSON pro odesílání v těle požadavku HTTP.
  • Volání PostAsync k odeslání JSobsahu ON na zadanou adresu URL. Toto je relativní adresa URL, která se přidá do HttpClient.BaseAddress.
  • Volání EnsureSuccessStatusCode , která vyvolá výjimku, pokud stavový kód odpovědi neznačí úspěch.

HttpClient podporuje také jiné typy obsahu. Příklad: MultipartContent a StreamContent. Úplný seznam podporovaného obsahu najdete v tématu HttpContent.

Následující příklad ukazuje požadavek HTTP PUT:

public async Task SaveItemAsync(TodoItem todoItem)
{
    var todoItemJson = new StringContent(
        JsonSerializer.Serialize(todoItem),
        Encoding.UTF8,
        Application.Json);

    using var httpResponseMessage =
        await _httpClient.PutAsync($"/api/TodoItems/{todoItem.Id}", todoItemJson);

    httpResponseMessage.EnsureSuccessStatusCode();
}

Předchozí kód je podobný příkladu POST. Metoda SaveItemAsync volá PutAsync místo PostAsync.

Následující příklad ukazuje požadavek HTTP DELETE:

public async Task DeleteItemAsync(long itemId)
{
    using var httpResponseMessage =
        await _httpClient.DeleteAsync($"/api/TodoItems/{itemId}");

    httpResponseMessage.EnsureSuccessStatusCode();
}

V předchozím kódu DeleteItemAsync metoda volá DeleteAsync. Vzhledem k tomu, že požadavky HTTP DELETE obvykle neobsahují žádný text, DeleteAsync metoda neposkytuje přetížení, které přijímá instanci HttpContent.

Další informace o použití různých příkazů HTTP s HttpClient, viz HttpClient.

Middleware odchozích požadavků

HttpClient má koncept delegování obslužných rutin, které lze propojit pro odchozí požadavky HTTP. IHttpClientFactory:

  • Zjednodušuje definování obslužných rutin, které se mají použít pro každého pojmenovaného klienta.
  • Podporuje registraci a řetězení více obslužných rutin pro vytvoření kanálu middlewaru odchozích požadavků. Každý z těchto obslužných rutin může provádět práci před a po odchozím požadavku. Tento vzor:
    • Podobá se kanálu příchozího middlewaru v ASP.NET Core.
    • Poskytuje mechanismus pro správu průřezových obav souvisejících s požadavky HTTP, například:
      • Mezipaměti
      • zpracování chyb
      • Serializace
      • protokolování

Vytvoření delegující obslužné rutiny:

  • Odvozovat od DelegatingHandler.
  • Přepsat SendAsync. Před předáním požadavku další obslužné rutině v kanálu spusťte kód:
public class ValidateHeaderHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (!request.Headers.Contains("X-API-KEY"))
        {
            return new HttpResponseMessage(HttpStatusCode.BadRequest)
            {
                Content = new StringContent(
                    "The API key header X-API-KEY is required.")
            };
        }

        return await base.SendAsync(request, cancellationToken);
    }
}

Předchozí kód zkontroluje, jestli je hlavička X-API-KEY v požadavku. Pokud X-API-KEY chybí, BadRequest vrátí se.

Do konfigurace pro jednu obslužnou rutinu lze přidat více než jednu obslužnou rutinu HttpClientMicrosoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandler:

builder.Services.AddTransient<ValidateHeaderHandler>();

builder.Services.AddHttpClient("HttpMessageHandler")
    .AddHttpMessageHandler<ValidateHeaderHandler>();

V předchozím kódu je zaregistrovaný ValidateHeaderHandler v DI. Po registraci AddHttpMessageHandler je možné volat předání typu pro obslužnou rutinu.

V pořadí, v jakém se mají spustit, je možné zaregistrovat více obslužných rutin. Každá obslužná rutina zabalí další obslužnou rutinu, dokud konečný HttpClientHandler požadavek nespustí:

builder.Services.AddTransient<SampleHandler1>();
builder.Services.AddTransient<SampleHandler2>();

builder.Services.AddHttpClient("MultipleHttpMessageHandlers")
    .AddHttpMessageHandler<SampleHandler1>()
    .AddHttpMessageHandler<SampleHandler2>();

V předchozím kódu se SampleHandler1 nejprve spustí před SampleHandler2.

Použití DI v middlewaru odchozích požadavků

Když IHttpClientFactory vytvoří novou obslužnou rutinu delegování, použije di ke splnění parametrů konstruktoru obslužné rutiny. IHttpClientFactoryvytvoří samostatný obor DI pro každou obslužnou rutinu, což může vést k překvapení chování, když obslužná rutina využívá vymezenou službu.

Představte si například následující rozhraní a jeho implementaci, které představuje úlohu jako operaci s identifikátorem: OperationId

public interface IOperationScoped
{
    string OperationId { get; }
}

public class OperationScoped : IOperationScoped
{
    public string OperationId { get; } = Guid.NewGuid().ToString()[^4..];
}

Jak napovídá název, IOperationScoped zaregistruje se v DI s využitím vymezené doby života:

builder.Services.AddScoped<IOperationScoped, OperationScoped>();

Následující delegování obslužné rutiny využívá a používá IOperationScoped k nastavení X-OPERATION-ID hlavičky pro odchozí požadavek:

public class OperationHandler : DelegatingHandler
{
    private readonly IOperationScoped _operationScoped;

    public OperationHandler(IOperationScoped operationScoped) =>
        _operationScoped = operationScoped;

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.Headers.Add("X-OPERATION-ID", _operationScoped.OperationId);

        return await base.SendAsync(request, cancellationToken);
    }
}

HttpRequestsSample Ve stažení přejděte na /Operation stránku a aktualizujte ji. Hodnota rozsahu požadavku se změní pro každý požadavek, ale hodnota oboru obslužné rutiny se změní pouze každých 5 sekund.

Obslužné rutiny můžou záviset na službách libovolného rozsahu. Služby, na které obslužné rutiny závisejí, jsou uvolněny při odstranění obslužné rutiny.

Ke sdílení stavu jednotlivých požadavků s obslužnými rutinami zpráv použijte jeden z následujících přístupů:

Použití obslužných rutin založených na Polly

IHttpClientFactory se integruje s knihovnou Polly třetích stran. Polly je komplexní knihovna odolnosti a přechodného zpracování chyb pro .NET. Umožňuje vývojářům vyjádřit zásady, jako jsou opakování, jistič, vypršení časového limitu, izolace bulkheadů a náhradní zařízení plynulým a bezpečným způsobem.

Metody rozšíření jsou k dispozici pro povolení použití zásad Polly s nakonfigurovanými HttpClient instancemi. Rozšíření Polly podporují přidávání obslužných rutin založených na Polly do klientů. Polly vyžaduje balíček NuGet Microsoft.Extensions.Http.Polly .

Zpracování přechodných chyb

K chybám obvykle dochází, když jsou externí volání HTTP přechodná. AddTransientHttpErrorPolicy umožňuje definovat zásadu pro zpracování přechodných chyb. Zásady nakonfigurované pomocí AddTransientHttpErrorPolicy zpracování následujících odpovědí:

AddTransientHttpErrorPolicy poskytuje přístup k objektu nakonfigurovaného PolicyBuilder pro zpracování chyb představujících možnou přechodnou chybu:

builder.Services.AddHttpClient("PollyWaitAndRetry")
    .AddTransientHttpErrorPolicy(policyBuilder =>
        policyBuilder.WaitAndRetryAsync(
            3, retryNumber => TimeSpan.FromMilliseconds(600)));

V předchozím kódu je definována WaitAndRetryAsync zásada. Neúspěšné požadavky se budou opakovat až třikrát se zpožděním 600 ms mezi pokusy.

Dynamické výběr zásad

Metody rozšíření jsou k dispozici pro přidání obslužných rutin založených na Polly, AddPolicyHandlernapříklad . AddPolicyHandler Následující přetížení zkontroluje požadavek a rozhodne, které zásady se mají použít:

var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
    TimeSpan.FromSeconds(10));
var longTimeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
    TimeSpan.FromSeconds(30));

builder.Services.AddHttpClient("PollyDynamic")
    .AddPolicyHandler(httpRequestMessage =>
        httpRequestMessage.Method == HttpMethod.Get ? timeoutPolicy : longTimeoutPolicy);

Pokud je odchozím požadavkem v předchozím kódu HTTP GET, použije se časový limit 10 sekund. Pro jakoukoli jinou metodu HTTP se použije časový limit 30 sekund.

Přidání více obslužných rutin Polly

Vnoření zásad Polly je běžné:

builder.Services.AddHttpClient("PollyMultiple")
    .AddTransientHttpErrorPolicy(policyBuilder =>
        policyBuilder.RetryAsync(3))
    .AddTransientHttpErrorPolicy(policyBuilder =>
        policyBuilder.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));

V předchozím příkladu:

  • Přidají se dva obslužné rutiny.
  • První obslužná rutina používá AddTransientHttpErrorPolicy k přidání zásady opakování. Neúspěšné požadavky se budou opakovat až třikrát.
  • Druhé AddTransientHttpErrorPolicy volání přidá zásadu jističe. Další externí požadavky se zablokují po dobu 30 sekund, pokud dojde k 5 neúspěšným pokusům. Zásady jističe jsou stavové. Všechna volání prostřednictvím tohoto klienta sdílejí stejný stav okruhu.

Přidání zásad z registru Polly

Přístup ke správě pravidelně používaných zásad je definovat je jednou a zaregistrovat je v PolicyRegistryprogramu . Příklad:

var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
    TimeSpan.FromSeconds(10));
var longTimeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
    TimeSpan.FromSeconds(30));

var policyRegistry = builder.Services.AddPolicyRegistry();

policyRegistry.Add("Regular", timeoutPolicy);
policyRegistry.Add("Long", longTimeoutPolicy);

builder.Services.AddHttpClient("PollyRegistryRegular")
    .AddPolicyHandlerFromRegistry("Regular");

builder.Services.AddHttpClient("PollyRegistryLong")
    .AddPolicyHandlerFromRegistry("Long");

V předchozím kódu:

  • Do registru Polly se přidají dvě zásady Regular a Longdo registru Polly.
  • AddPolicyHandlerFromRegistry nakonfiguruje jednotlivé pojmenované klienty tak, aby tyto zásady používaly z registru Polly.

Další informace o IHttpClientFactory integraci a integraci Polly najdete na wikiwebu Polly.

Správa klienta HttpClient a životnosti

Nová HttpClient instance se vrátí pokaždé, když CreateClient je volána na IHttpClientFactory. Vytvoří se HttpMessageHandler pro pojmenovaného klienta. Továrna spravuje životnosti HttpMessageHandler instancí.

IHttpClientFactory vytváří fondy HttpMessageHandler instancí vytvořených továrnou, aby se snížila spotřeba prostředků. Pokud HttpMessageHandler jeho životnost nevypršela, může být instance z fondu HttpClient znovu použita.

Sdružování obslužných rutin je žádoucí, protože každá obslužná rutina obvykle spravuje vlastní základní připojení HTTP. Vytvoření více obslužných rutin, než je potřeba, může vést ke zpoždění připojení. Některé obslužné rutiny také trvale udržují připojení otevřená, což může obslužné rutině zabránit v reakci na změny DNS (Domain Name System).

Výchozí životnost obslužné rutiny je dvě minuty. Výchozí hodnotu lze přepsat na základě pojmenovaného klienta:

builder.Services.AddHttpClient("HandlerLifetime")
    .SetHandlerLifetime(TimeSpan.FromMinutes(5));

HttpClient Instance lze obecně považovat za objekty .NET, které nevyžadují odstranění. Odstranění zruší odchozí požadavky a zaručuje, že danou HttpClient instanci nelze po volání Disposepoužít . IHttpClientFactory sleduje a odstraňuje prostředky používané instancemi HttpClient .

Udržování jedné HttpClient instance naživu po dlouhou dobu je běžný vzor používaný před vznikem IHttpClientFactory. Tento model se po migraci na IHttpClientFactory.

Alternativy k IHttpClientFactory

Použití IHttpClientFactory v aplikaci s podporou DI se vyhne:

  • Problémy s vyčerpáním prostředků sdružováním HttpMessageHandler instancí
  • Zastaralé problémy DNS cyklickými HttpMessageHandler instancemi v pravidelných intervalech.

Existují alternativní způsoby řešení předchozích problémů pomocí dlouhodobé SocketsHttpHandler instance.

  • Vytvořte instanci SocketsHttpHandler , kdy se aplikace spustí a použije ji pro životnost aplikace.
  • Nakonfigurujte PooledConnectionLifetime odpovídající hodnotu na základě času aktualizace DNS.
  • Podle potřeby vytvořte HttpClient instance.new HttpClient(handler, disposeHandler: false)

Předchozí přístupy řeší problémy správy prostředků, které IHttpClientFactory řeší podobným způsobem.

  • Sdílené SocketsHttpHandler připojení mezi HttpClient instancemi. Toto sdílení zabraňuje vyčerpání soketů.
  • Cykly SocketsHttpHandler připojení podle toho, aby nedocházelo k PooledConnectionLifetime zastaralým problémům s DNS.

Protokolování

Klienti vytvořená prostřednictvím IHttpClientFactory zpráv protokolu záznamů pro všechny požadavky. Povolte odpovídající úroveň informací v konfiguraci protokolování, abyste viděli výchozí zprávy protokolu. Další protokolování, například protokolování hlaviček požadavků, je zahrnuto pouze na úrovni trasování.

Kategorie protokolu používaná pro každého klienta obsahuje název klienta. Klient s názvem MyNamedClient například protokoluje zprávy s kategorií System.Net.Http.HttpClient.MyNamedClient. Logická obslužná rutina. Zprávy s příponou LogicalHandler se vyskytují mimo kanál obslužné rutiny požadavku. Na požadavku se zprávy protokolují předtím, než je zpracují ostatní obslužné rutiny v kanálu. V odpovědi se zprávy zaprotokolují po přijetí odpovědi jinými obslužnými rutinami kanálu.

Protokolování také probíhá v kanálu obslužné rutiny požadavku. V příkladu MyNamedClient se tyto zprávy protokolují s kategorií protokolu System.Net.Http.HttpClient.MyNamedClient. ClientHandler. U požadavku k tomu dochází po spuštění všech ostatních obslužných rutin a bezprostředně před odesláním požadavku. Toto protokolování v odpovědi zahrnuje stav odpovědi před tím, než projde zpět kanálem obslužné rutiny.

Povolení protokolování mimo kanál a uvnitř kanálu umožňuje kontrolu změn provedených jinými obslužnými rutinami kanálu. To může zahrnovat změny hlaviček požadavků nebo stavového kódu odpovědi.

Zahrnutí názvu klienta do kategorie protokolu umožňuje filtrování protokolů pro konkrétní pojmenované klienty.

Konfigurace obslužné rutiny HttpMessage

Může být nutné řídit konfiguraci vnitřního HttpMessageHandler prostředí používaného klientem.

Vrátí IHttpClientBuilder se při přidávání pojmenovaných nebo zadaných klientů. Metodu ConfigurePrimaryHttpMessageHandler rozšíření lze použít k definování delegáta. Delegát se používá k vytvoření a konfiguraci primárního HttpMessageHandler klienta, který tento klient používá:

builder.Services.AddHttpClient("ConfiguredHttpMessageHandler")
    .ConfigurePrimaryHttpMessageHandler(() =>
        new HttpClientHandler
        {
            AllowAutoRedirect = true,
            UseDefaultCredentials = true
        });

CookieS

Instance ve HttpMessageHandler fondu mají za CookieContainer následek sdílení objektů. CookieContainer Neočekávané sdílení objektů často vede k nesprávnému kódu. U aplikací, které vyžadují cookie, zvažte jednu z těchto možností:

  • Zakázání automatického cookie zpracování
  • Vyhnout IHttpClientFactory

Volání ConfigurePrimaryHttpMessageHandler , které zakáže automatické cookie zpracování:

builder.Services.AddHttpClient("NoAutomaticCookies")
    .ConfigurePrimaryHttpMessageHandler(() =>
        new HttpClientHandler
        {
            UseCookies = false
        });

Použití IHttpClientFactory v konzolové aplikaci

V aplikaci konzoly přidejte do projektu následující odkazy na balíčky:

V následujícím příkladu:

  • IHttpClientFactory a GitHubService jsou zaregistrované v kontejneru služby obecného hostitele .
  • GitHubService je požadována z DI, která následně požaduje instanci IHttpClientFactory.
  • GitHubService používá IHttpClientFactory k vytvoření instance , kterou HttpClientpoužívá k načtení větví GitHubu na webu Docs.
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

var host = new HostBuilder()
    .ConfigureServices(services =>
    {
        services.AddHttpClient();
        services.AddTransient<GitHubService>();
    })
    .Build();

try
{
    var gitHubService = host.Services.GetRequiredService<GitHubService>();
    var gitHubBranches = await gitHubService.GetAspNetCoreDocsBranchesAsync();

    Console.WriteLine($"{gitHubBranches?.Count() ?? 0} GitHub Branches");

    if (gitHubBranches is not null)
    {
        foreach (var gitHubBranch in gitHubBranches)
        {
            Console.WriteLine($"- {gitHubBranch.Name}");
        }
    }
}
catch (Exception ex)
{
    host.Services.GetRequiredService<ILogger<Program>>()
        .LogError(ex, "Unable to load branches from GitHub.");
}

public class GitHubService
{
    private readonly IHttpClientFactory _httpClientFactory;

    public GitHubService(IHttpClientFactory httpClientFactory) =>
        _httpClientFactory = httpClientFactory;

    public async Task<IEnumerable<GitHubBranch>?> GetAspNetCoreDocsBranchesAsync()
    {
        var httpRequestMessage = new HttpRequestMessage(
            HttpMethod.Get,
            "https://api.github.com/repos/dotnet/AspNetCore.Docs/branches")
        {
            Headers =
            {
                { "Accept", "application/vnd.github.v3+json" },
                { "User-Agent", "HttpRequestsConsoleSample" }
            }
        };

        var httpClient = _httpClientFactory.CreateClient();
        var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);

        httpResponseMessage.EnsureSuccessStatusCode();

        using var contentStream =
            await httpResponseMessage.Content.ReadAsStreamAsync();
        
        return await JsonSerializer.DeserializeAsync
            <IEnumerable<GitHubBranch>>(contentStream);
    }
}

public record GitHubBranch(
    [property: JsonPropertyName("name")] string Name);

Middleware šíření hlaviček

Šíření hlaviček je middleware ASP.NET Core, který rozšíří hlavičky HTTP z příchozího požadavku na odchozí HttpClient požadavky. Použití šíření hlaviček:

  • Nainstalujte balíček Microsoft.AspNetCore.HeaderPropagation.

  • Konfigurace kanálu middlewaru HttpClient v Program.cs:

    // Add services to the container.
    builder.Services.AddControllers();
    
    builder.Services.AddHttpClient("PropagateHeaders")
        .AddHeaderPropagation();
    
    builder.Services.AddHeaderPropagation(options =>
    {
        options.Headers.Add("X-TraceId");
    });
    
    var app = builder.Build();
    
    // Configure the HTTP request pipeline.
    app.UseHttpsRedirection();
    
    app.UseHeaderPropagation();
    
    app.MapControllers();
    
  • Odchozí požadavky můžete provádět pomocí nakonfigurované HttpClient instance, která zahrnuje přidané hlavičky.

Další prostředky

Kirk Larkin, Steve Gordon, Glenn Condron a Ryan Nowak.

Můžete IHttpClientFactory ho zaregistrovat a použít ke konfiguraci a vytvoření HttpClient instancí v aplikaci. IHttpClientFactory nabízí následující výhody:

  • Poskytuje centrální umístění pro pojmenování a konfiguraci logických instancí HttpClient. Klient s názvem GitHub může být například zaregistrovaný a nakonfigurovaný pro přístup k GitHubu. Pro obecný přístup je možné zaregistrovat výchozího klienta.
  • Kodifikuje koncept odchozího middlewaru prostřednictvím delegování obslužných rutin v HttpClient. Poskytuje rozšíření pro middleware založený na Polly, které využívají delegování obslužných rutin v HttpClient.
  • Spravuje sdružování a životnost základních HttpClientMessageHandler instancí. Automatická správa zabraňuje běžným problémům s DNS (Domain Name System), ke kterým dochází při ruční správě HttpClient životnosti.
  • Přidá konfigurovatelné protokolování (prostřednictvím ILogger) pro všechny požadavky odeslané prostřednictvím klientů vytvořených továrnou.

Zobrazení nebo stažení vzorového kódu (postup stažení)

Vzorový kód v této verzi tématu používá System.Text.Json k deserializaci JSobsahu ON vráceného v odpovědích HTTP. Pro ukázky, které používají Json.NET , a ReadAsAsync<T>pomocí selektoru verzí vyberte verzi 2.x tohoto tématu.

Spotřeby

V aplikaci můžete použít několik způsobů IHttpClientFactory :

Nejlepší přístup závisí na požadavcích aplikace.

Základní použití

IHttpClientFactory lze registrovat voláním 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.

O IHttpClientFactory injektáž závislostí (DI) je možné požádat. Následující kód používá IHttpClientFactory k vytvoření 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/dotnet/AspNetCore.Docs/branches");
        request.Headers.Add("Accept", "application/vnd.github.v3+json");
        request.Headers.Add("User-Agent", "HttpClientFactory-Sample");

        var client = _clientFactory.CreateClient();

        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            using var responseStream = await response.Content.ReadAsStreamAsync();
            Branches = await JsonSerializer.DeserializeAsync
                <IEnumerable<GitHubBranch>>(responseStream);
        }
        else
        {
            GetBranchesError = true;
            Branches = Array.Empty<GitHubBranch>();
        }
    }
}

Použití IHttpClientFactory podobné jako v předchozím příkladu je dobrým způsobem, jak refaktorovat existující aplikaci. Nemá žádný vliv na způsob HttpClient použití. V místech, kde HttpClient se instance vytvářejí v existující aplikaci, nahraďte tyto výskyty voláními CreateClient.

Pojmenovaní klienti

Pojmenovaní klienti jsou dobrou volbou, když:

  • Aplikace vyžaduje mnoho různých použití HttpClient.
  • Mnoho HttpClientz nich má jinou konfiguraci.

Konfiguraci pojmenovaného HttpClient názvu lze zadat během registrace v 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");
});

V předchozím kódu je klient nakonfigurovaný takto:

  • Základní adresa https://api.github.com/.
  • Dvě hlavičky potřebné pro práci s rozhraním API GitHubu

CreateClient

Pokaždé CreateClient se volá:

  • Vytvoří se nová instance HttpClient .
  • Volá se akce konfigurace.

Pokud chcete vytvořit pojmenovaného klienta, předejte jeho název do CreateClient:

public class NamedClientModel : PageModel
{
    private readonly IHttpClientFactory _clientFactory;

    public IEnumerable<GitHubPullRequest> PullRequests { get; private set; }

    public bool GetPullRequestsError { get; private set; }

    public bool HasPullRequests => PullRequests.Any();

    public NamedClientModel(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }

    public async Task OnGet()
    {
        var request = new HttpRequestMessage(HttpMethod.Get,
            "repos/dotnet/AspNetCore.Docs/pulls");

        var client = _clientFactory.CreateClient("github");

        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            using var responseStream = await response.Content.ReadAsStreamAsync();
            PullRequests = await JsonSerializer.DeserializeAsync
                    <IEnumerable<GitHubPullRequest>>(responseStream);
        }
        else
        {
            GetPullRequestsError = true;
            PullRequests = Array.Empty<GitHubPullRequest>();
        }
    }
}

V předchozím kódu požadavek nemusí zadávat název hostitele. Kód může předat pouze cestu, protože se používá základní adresa nakonfigurovaná pro klienta.

Typoví klienti

Typoví klienti:

  • Poskytněte stejné možnosti jako pojmenovaní klienti bez nutnosti používat řetězce jako klíče.
  • Poskytuje nápovědu intelliSense a kompilátoru při využívání klientů.
  • Zadejte jedno umístění pro konfiguraci a interakci s určitým HttpClient. Může se například použít jeden typ klienta:
    • Pro jeden back-endový koncový bod.
    • Zapouzdření veškeré logiky, která se zabývá koncovým bodem.
  • Pracujte s DI a můžete je v aplikaci vsunou tam, kde je to potřeba.

Zadaný klient přijímá parametr v jeho konstruktoru HttpClient :

public class GitHubService
{
    public HttpClient Client { get; }

    public GitHubService(HttpClient client)
    {
        client.BaseAddress = new Uri("https://api.github.com/");
        // GitHub API versioning
        client.DefaultRequestHeaders.Add("Accept",
            "application/vnd.github.v3+json");
        // GitHub requires a user-agent
        client.DefaultRequestHeaders.Add("User-Agent",
            "HttpClientFactory-Sample");

        Client = client;
    }

    public async Task<IEnumerable<GitHubIssue>> GetAspNetDocsIssues()
    {
        return await Client.GetFromJsonAsync<IEnumerable<GitHubIssue>>(
          "/repos/aspnet/AspNetCore.Docs/issues?state=open&sort=created&direction=desc");
    }
}

V předchozím kódu:

  • Konfigurace se přesune do zadaného klienta.
  • Objekt HttpClient je vystavený jako veřejná vlastnost.

Metody specifické pro rozhraní API je možné vytvořit, které zpřístupňují HttpClient funkce. Například GetAspNetDocsIssues metoda zapouzdřuje kód pro načtení otevřených problémů.

Následující volání AddHttpClientStartup.ConfigureServices kódu pro registraci typové klientské třídy:

services.AddHttpClient<GitHubService>();

Zadaný klient je zaregistrovaný jako přechodný s DI. V předchozím kódu AddHttpClient se zaregistruje GitHubService jako přechodná služba. Tato registrace používá metodu továrny k:

  1. Vytvořte instanci HttpClient.
  2. Vytvoření instance , GitHubServicepředání instance HttpClient do jeho konstruktoru.

Zadaný klient se dá vloženého a spotřebovávat přímo:

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

Konfiguraci pro typového klienta je možné zadat při registraci v Startup.ConfigureServicesnástroji , nikoli v konstruktoru zadaného klienta:

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");
});

Zapouzdření HttpClient lze zapouzdřovat v rámci zadaného klienta. Místo zveřejnění jako vlastnosti definujte metodu, která interně volá HttpClient instanci:

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

V předchozím kódu je uložen HttpClient v privátním poli. Přístup k této HttpClient metodě je určen veřejnou GetRepos metodou.

Vygenerované klienty

IHttpClientFactory lze použít v kombinaci s knihovnami třetích stran, jako je například Refit. Refit je REST knihovna pro .NET. REST Převádí rozhraní API na živá rozhraní. Implementace rozhraní se generuje dynamicky RestServicepomocí , pomocí HttpClient k provedení externích volání HTTP.

Rozhraní a odpověď jsou definovány tak, aby představovaly externí rozhraní API a jeho odpověď:

public interface IHelloClient
{
    [Get("/helloworld")]
    Task<Reply> GetMessageAsync();
}

public class Reply
{
    public string Message { get; set; }
}

Typový klient je možné přidat pomocí nástroje Refit k vygenerování implementace:

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

Definované rozhraní lze v případě potřeby využívat s implementací, kterou poskytuje DI a 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();
    }
}

Vytváření požadavků POST, PUT a DELETE

V předchozích příkladech používají všechny požadavky HTTP příkaz GET HTTP. HttpClient také podporuje další příkazy HTTP, včetně:

  • POST
  • PUT
  • SMAZÁNÍ
  • PATCH

Úplný seznam podporovaných příkazů HTTP najdete v tématu HttpMethod.

Následující příklad ukazuje, jak vytvořit požadavek HTTP POST:

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

V předchozím kódu CreateItemAsync metoda:

  • Serializuje TodoItem parametr on JSon using System.Text.Json. To používá instanci JsonSerializerOptions ke konfiguraci procesu serializace.
  • Vytvoří instanci StringContent pro zabalení serializované JSON pro odesílání v těle požadavku HTTP.
  • Volání PostAsync k odeslání JSobsahu ON na zadanou adresu URL. Toto je relativní adresa URL, která se přidá do HttpClient.BaseAddress.
  • Volání EnsureSuccessStatusCode vyvolání výjimky, pokud stavový kód odpovědi nezjistí úspěch.

HttpClient podporuje také jiné typy obsahu. Příklad: MultipartContent a StreamContent. Úplný seznam podporovaného obsahu najdete v tématu HttpContent.

Následující příklad ukazuje požadavek HTTP PUT:

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

Předchozí kód je velmi podobný příkladu POST. Metoda SaveItemAsync volá PutAsync místo PostAsync.

Následující příklad ukazuje požadavek HTTP DELETE:

public async Task DeleteItemAsync(long itemId)
{
    using var httpResponse =
        await _httpClient.DeleteAsync($"/api/TodoItems/{itemId}");

    httpResponse.EnsureSuccessStatusCode();
}

V předchozím kódu DeleteItemAsync metoda volá DeleteAsync. Vzhledem k tomu, že požadavky HTTP DELETE obvykle neobsahují žádný text, DeleteAsync metoda neposkytuje přetížení, které přijímá instanci HttpContent.

Další informace o použití různých příkazů HTTP s HttpClient, viz HttpClient.

Middleware odchozích požadavků

HttpClient má koncept delegování obslužných rutin, které lze propojit pro odchozí požadavky HTTP. IHttpClientFactory:

  • Zjednodušuje definování obslužných rutin, které se mají použít pro každého pojmenovaného klienta.
  • Podporuje registraci a řetězení více obslužných rutin pro vytvoření kanálu middlewaru odchozích požadavků. Každý z těchto obslužných rutin může provádět práci před a po odchozím požadavku. Tento vzor:
    • Podobá se kanálu příchozího middlewaru v ASP.NET Core.
    • Poskytuje mechanismus pro správu průřezových obav souvisejících s požadavky HTTP, například:
      • Mezipaměti
      • zpracování chyb
      • Serializace
      • protokolování

Vytvoření delegující obslužné rutiny:

  • Odvozovat od DelegatingHandler.
  • Přepsat SendAsync. Před předáním požadavku další obslužné rutině v kanálu spusťte kód:
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);
    }
}

Předchozí kód zkontroluje, jestli je hlavička X-API-KEY v požadavku. Pokud X-API-KEY chybí, BadRequest vrátí se.

Do konfigurace pro jednu obslužnou rutinu lze přidat více než jednu obslužnou rutinu HttpClientMicrosoft.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.

V předchozím kódu je zaregistrovaný ValidateHeaderHandler v DI. Po registraci AddHttpMessageHandler je možné volat předání typu pro obslužnou rutinu.

V pořadí, v jakém se mají spustit, je možné zaregistrovat více obslužných rutin. Každá obslužná rutina zabalí další obslužnou rutinu, dokud konečný HttpClientHandler požadavek nespustí:

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

Použití DI v middlewaru odchozích požadavků

Když IHttpClientFactory vytvoří novou obslužnou rutinu delegování, použije di ke splnění parametrů konstruktoru obslužné rutiny. IHttpClientFactoryvytvoří samostatný obor DI pro každou obslužnou rutinu, což může vést k překvapení chování, když obslužná rutina využívá vymezenou službu.

Představte si například následující rozhraní a jeho implementaci, které představuje úlohu jako operaci s identifikátorem: OperationId

public interface IOperationScoped 
{
    string OperationId { get; }
}

public class OperationScoped : IOperationScoped
{
    public string OperationId { get; } = Guid.NewGuid().ToString()[^4..];
}

Jak napovídá název, IOperationScoped zaregistruje se v DI s využitím vymezené doby života:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<TodoContext>(options =>
        options.UseInMemoryDatabase("TodoItems"));

    services.AddHttpContextAccessor();

    services.AddHttpClient<TodoClient>((sp, httpClient) =>
    {
        var httpRequest = sp.GetRequiredService<IHttpContextAccessor>().HttpContext.Request;

        // For sample purposes, assume TodoClient is used in the context of an incoming request.
        httpClient.BaseAddress = new Uri(UriHelper.BuildAbsolute(httpRequest.Scheme,
                                         httpRequest.Host, httpRequest.PathBase));
        httpClient.Timeout = TimeSpan.FromSeconds(5);
    });

    services.AddScoped<IOperationScoped, OperationScoped>();
    
    services.AddTransient<OperationHandler>();
    services.AddTransient<OperationResponseHandler>();

    services.AddHttpClient("Operation")
        .AddHttpMessageHandler<OperationHandler>()
        .AddHttpMessageHandler<OperationResponseHandler>()
        .SetHandlerLifetime(TimeSpan.FromSeconds(5));

    services.AddControllers();
    services.AddRazorPages();
}

Následující delegování obslužné rutiny využívá a používá IOperationScoped k nastavení X-OPERATION-ID hlavičky pro odchozí požadavek:

public class OperationHandler : DelegatingHandler
{
    private readonly IOperationScoped _operationService;

    public OperationHandler(IOperationScoped operationScoped)
    {
        _operationService = operationScoped;
    }

    protected async override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.Headers.Add("X-OPERATION-ID", _operationService.OperationId);

        return await base.SendAsync(request, cancellationToken);
    }
}

HttpRequestsSample Ve stažení přejděte na /Operation stránku a aktualizujte ji. Hodnota rozsahu požadavku se změní pro každý požadavek, ale hodnota oboru obslužné rutiny se změní pouze každých 5 sekund.

Obslužné rutiny můžou záviset na službách libovolného rozsahu. Služby, na které obslužné rutiny závisejí, jsou uvolněny při odstranění obslužné rutiny.

Ke sdílení stavu jednotlivých požadavků s obslužnými rutinami zpráv použijte jeden z následujících přístupů:

Použití obslužných rutin založených na Polly

IHttpClientFactory se integruje s knihovnou Polly třetích stran. Polly je komplexní knihovna odolnosti a přechodného zpracování chyb pro .NET. Umožňuje vývojářům vyjádřit zásady, jako jsou opakování, jistič, vypršení časového limitu, izolace bulkheadů a náhradní zařízení plynulým a bezpečným způsobem.

Metody rozšíření jsou k dispozici pro povolení použití zásad Polly s nakonfigurovanými HttpClient instancemi. Rozšíření Polly podporují přidávání obslužných rutin založených na Polly do klientů. Polly vyžaduje balíček NuGet Microsoft.Extensions.Http.Polly .

Zpracování přechodných chyb

K chybám obvykle dochází, když jsou externí volání HTTP přechodná. AddTransientHttpErrorPolicy umožňuje definovat zásadu pro zpracování přechodných chyb. Zásady nakonfigurované pomocí AddTransientHttpErrorPolicy zpracování následujících odpovědí:

AddTransientHttpErrorPolicy poskytuje přístup k objektu nakonfigurovaného PolicyBuilder pro zpracování chyb představujících možnou přechodnou chybu:

public void ConfigureServices(IServiceCollection services)
{           
    services.AddHttpClient<UnreliableEndpointCallerService>()
        .AddTransientHttpErrorPolicy(p => 
            p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));

    // Remaining code deleted for brevity.

V předchozím kódu je definována WaitAndRetryAsync zásada. Neúspěšné požadavky se budou opakovat až třikrát se zpožděním 600 ms mezi pokusy.

Dynamické výběr zásad

Metody rozšíření jsou k dispozici pro přidání obslužných rutin založených na Polly, AddPolicyHandlernapříklad . AddPolicyHandler Následující přetížení zkontroluje požadavek a rozhodne, které zásady se mají použít:

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);

Pokud je odchozím požadavkem v předchozím kódu HTTP GET, použije se časový limit 10 sekund. Pro jakoukoli jinou metodu HTTP se použije časový limit 30 sekund.

Přidání více obslužných rutin Polly

Vnoření zásad Polly je běžné:

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

V předchozím příkladu:

  • Přidají se dva obslužné rutiny.
  • První obslužná rutina používá AddTransientHttpErrorPolicy k přidání zásady opakování. Neúspěšné požadavky se budou opakovat až třikrát.
  • Druhé AddTransientHttpErrorPolicy volání přidá zásadu jističe. Další externí požadavky se zablokují po dobu 30 sekund, pokud dojde k 5 neúspěšným pokusům. Zásady jističe jsou stavové. Všechna volání prostřednictvím tohoto klienta sdílejí stejný stav okruhu.

Přidání zásad z registru Polly

Přístup ke správě pravidelně používaných zásad je definovat je jednou a zaregistrovat je v PolicyRegistryprogramu .

V následujícím kódu:

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.

Další informace o IHttpClientFactory integraci a integraci Polly najdete na wikiwebu Polly.

Správa klienta HttpClient a životnosti

Nová HttpClient instance se vrátí pokaždé, když CreateClient je volána na IHttpClientFactory. Vytvoří se HttpMessageHandler pro pojmenovaného klienta. Továrna spravuje životnosti HttpMessageHandler instancí.

IHttpClientFactory vytváří fondy HttpMessageHandler instancí vytvořených továrnou, aby se snížila spotřeba prostředků. Pokud HttpMessageHandler jeho životnost nevypršela, může být instance z fondu HttpClient znovu použita.

Sdružování obslužných rutin je žádoucí, protože každá obslužná rutina obvykle spravuje vlastní základní připojení HTTP. Vytvoření více obslužných rutin, než je potřeba, může vést ke zpoždění připojení. Některé obslužné rutiny také trvale udržují připojení otevřená, což může obslužné rutině zabránit v reakci na změny DNS (Domain Name System).

Výchozí životnost obslužné rutiny je dvě minuty. Výchozí hodnotu lze přepsat na základě pojmenovaného klienta:

public void ConfigureServices(IServiceCollection services)
{           
    services.AddHttpClient("extendedhandlerlifetime")
        .SetHandlerLifetime(TimeSpan.FromMinutes(5));

    // Remaining code deleted for brevity.

HttpClient Instance lze obecně považovat za objekty .NET, které nevyžadují odstranění. Odstranění zruší odchozí požadavky a zaručuje, že danou HttpClient instanci nelze po volání Disposepoužít . IHttpClientFactory sleduje a odstraňuje prostředky používané instancemi HttpClient .

Udržování jedné HttpClient instance naživu po dlouhou dobu je běžný vzor používaný před vznikem IHttpClientFactory. Tento model se po migraci na IHttpClientFactory.

Alternativy k IHttpClientFactory

Použití IHttpClientFactory v aplikaci s podporou DI se vyhne:

  • Problémy s vyčerpáním prostředků sdružováním HttpMessageHandler instancí
  • Zastaralé problémy DNS cyklickými HttpMessageHandler instancemi v pravidelných intervalech.

Existují alternativní způsoby řešení předchozích problémů pomocí dlouhodobé SocketsHttpHandler instance.

  • Vytvořte instanci SocketsHttpHandler , kdy se aplikace spustí a použije ji pro životnost aplikace.
  • Nakonfigurujte PooledConnectionLifetime odpovídající hodnotu na základě času aktualizace DNS.
  • Podle potřeby vytvořte HttpClient instance.new HttpClient(handler, disposeHandler: false)

Předchozí přístupy řeší problémy správy prostředků, které IHttpClientFactory řeší podobným způsobem.

  • Sdílené SocketsHttpHandler připojení mezi HttpClient instancemi. Toto sdílení zabraňuje vyčerpání soketů.
  • Cykly SocketsHttpHandler připojení podle toho, aby nedocházelo k PooledConnectionLifetime zastaralým problémům s DNS.

CookieS

Instance ve HttpMessageHandler fondu mají za CookieContainer následek sdílení objektů. CookieContainer Neočekávané sdílení objektů často vede k nesprávnému kódu. U aplikací, které vyžadují cookie, zvažte jednu z těchto možností:

  • Zakázání automatického cookie zpracování
  • Vyhnout IHttpClientFactory

Volání ConfigurePrimaryHttpMessageHandler , které zakáže automatické cookie zpracování:

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

Protokolování

Klienti vytvořená prostřednictvím IHttpClientFactory zpráv protokolu záznamů pro všechny požadavky. Povolte odpovídající úroveň informací v konfiguraci protokolování, abyste viděli výchozí zprávy protokolu. Další protokolování, například protokolování hlaviček požadavků, je zahrnuto pouze na úrovni trasování.

Kategorie protokolu používaná pro každého klienta obsahuje název klienta. Klient s názvem MyNamedClient například protokoluje zprávy s kategorií System.Net.Http.HttpClient.MyNamedClient. Logická obslužná rutina. Zprávy s příponou LogicalHandler se vyskytují mimo kanál obslužné rutiny požadavku. Na požadavku se zprávy protokolují předtím, než je zpracují ostatní obslužné rutiny v kanálu. V odpovědi se zprávy zaprotokolují po přijetí odpovědi jinými obslužnými rutinami kanálu.

Protokolování také probíhá v kanálu obslužné rutiny požadavku. V příkladu MyNamedClient se tyto zprávy protokolují s kategorií protokolu System.Net.Http.HttpClient.MyNamedClient. ClientHandler. U požadavku k tomu dochází po spuštění všech ostatních obslužných rutin a bezprostředně před odesláním požadavku. Toto protokolování v odpovědi zahrnuje stav odpovědi před tím, než projde zpět kanálem obslužné rutiny.

Povolení protokolování mimo kanál a uvnitř kanálu umožňuje kontrolu změn provedených jinými obslužnými rutinami kanálu. To může zahrnovat změny hlaviček požadavků nebo stavového kódu odpovědi.

Zahrnutí názvu klienta do kategorie protokolu umožňuje filtrování protokolů pro konkrétní pojmenované klienty.

Konfigurace obslužné rutiny HttpMessage

Může být nutné řídit konfiguraci vnitřního HttpMessageHandler prostředí používaného klientem.

Vrátí IHttpClientBuilder se při přidávání pojmenovaných nebo zadaných klientů. Metodu ConfigurePrimaryHttpMessageHandler rozšíření lze použít k definování delegáta. Delegát se používá k vytvoření a konfiguraci primárního HttpMessageHandler klienta, který tento klient používá:

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

    // Remaining code deleted for brevity.

Použití IHttpClientFactory v konzolové aplikaci

V aplikaci konzoly přidejte do projektu následující odkazy na balíčky:

V následujícím příkladu:

  • IHttpClientFactory je zaregistrovaný v kontejneru služby obecného hostitele .
  • MyService vytvoří instanci klientské továrny ze služby, která slouží k vytvoření objektu HttpClient. HttpClient slouží k načtení webové stránky.
  • Main vytvoří obor pro spuštění metody služby GetPage a zápis prvních 500 znaků obsahu webové stránky do konzoly.
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
class Program
{
    static async Task<int> Main(string[] args)
    {
        var builder = new HostBuilder()
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHttpClient();
                services.AddTransient<IMyService, MyService>();
            }).UseConsoleLifetime();

        var host = builder.Build();

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

            Console.WriteLine(pageContent.Substring(0, 500));
        }
        catch (Exception ex)
        {
            var logger = host.Services.GetRequiredService<ILogger<Program>>();

            logger.LogError(ex, "An error occurred.");
        }

        return 0;
    }

    public interface IMyService
    {
        Task<string> GetPage();
    }

    public class MyService : IMyService
    {
        private readonly IHttpClientFactory _clientFactory;

        public MyService(IHttpClientFactory clientFactory)
        {
            _clientFactory = clientFactory;
        }

        public async Task<string> GetPage()
        {
            // Content from BBC One: Dr. Who website (©BBC)
            var request = new HttpRequestMessage(HttpMethod.Get,
                "https://www.bbc.co.uk/programmes/b006q2x0");
            var client = _clientFactory.CreateClient();
            var response = await client.SendAsync(request);

            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadAsStringAsync();
            }
            else
            {
                return $"StatusCode: {response.StatusCode}";
            }
        }
    }
}

Middleware šíření hlaviček

Šíření hlaviček je middleware ASP.NET Core, který rozšíří hlavičky HTTP z příchozího požadavku na odchozí požadavky klienta HTTP. Použití šíření hlaviček:

  • Odkaz na balíček Microsoft.AspNetCore.HeaderPropagation .

  • Nakonfigurujte middleware a HttpClient v 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();
        });
    }
    
  • Klient zahrnuje nakonfigurované hlavičky u odchozích požadavků:

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

Další prostředky

Kirk Larkin, Steve Gordon, Glenn Condron a Ryan Nowak.

Můžete IHttpClientFactory ho zaregistrovat a použít ke konfiguraci a vytvoření HttpClient instancí v aplikaci. IHttpClientFactory nabízí následující výhody:

  • Poskytuje centrální umístění pro pojmenování a konfiguraci logických instancí HttpClient. Klient s názvem GitHub může být například zaregistrovaný a nakonfigurovaný pro přístup k GitHubu. Pro obecný přístup je možné zaregistrovat výchozího klienta.
  • Kodifikuje koncept odchozího middlewaru prostřednictvím delegování obslužných rutin v HttpClient. Poskytuje rozšíření pro middleware založený na Polly, které využívají delegování obslužných rutin v HttpClient.
  • Spravuje sdružování a životnost základních HttpClientMessageHandler instancí. Automatická správa zabraňuje běžným problémům s DNS (Domain Name System), ke kterým dochází při ruční správě HttpClient životnosti.
  • Přidá konfigurovatelné protokolování (prostřednictvím ILogger) pro všechny požadavky odeslané prostřednictvím klientů vytvořených továrnou.

Zobrazení nebo stažení vzorového kódu (postup stažení)

Vzorový kód v této verzi tématu používá System.Text.Json k deserializaci JSobsahu ON vráceného v odpovědích HTTP. Pro ukázky, které používají Json.NET , a ReadAsAsync<T>pomocí selektoru verzí vyberte verzi 2.x tohoto tématu.

Spotřeby

V aplikaci můžete použít několik způsobů IHttpClientFactory :

Nejlepší přístup závisí na požadavcích aplikace.

Základní použití

IHttpClientFactory lze registrovat voláním 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.

O IHttpClientFactory injektáž závislostí (DI) je možné požádat. Následující kód používá IHttpClientFactory k vytvoření 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/dotnet/AspNetCore.Docs/branches");
        request.Headers.Add("Accept", "application/vnd.github.v3+json");
        request.Headers.Add("User-Agent", "HttpClientFactory-Sample");

        var client = _clientFactory.CreateClient();

        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            using var responseStream = await response.Content.ReadAsStreamAsync();
            Branches = await JsonSerializer.DeserializeAsync
                <IEnumerable<GitHubBranch>>(responseStream);
        }
        else
        {
            GetBranchesError = true;
            Branches = Array.Empty<GitHubBranch>();
        }
    }
}

Použití IHttpClientFactory podobné jako v předchozím příkladu je dobrým způsobem, jak refaktorovat existující aplikaci. Nemá žádný vliv na způsob HttpClient použití. V místech, kde HttpClient se instance vytvářejí v existující aplikaci, nahraďte tyto výskyty voláními CreateClient.

Pojmenovaní klienti

Pojmenovaní klienti jsou dobrou volbou, když:

  • Aplikace vyžaduje mnoho různých použití HttpClient.
  • Mnoho HttpClientz nich má jinou konfiguraci.

Konfiguraci pojmenovaného HttpClient názvu lze zadat během registrace v 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");
});

V předchozím kódu je klient nakonfigurovaný takto:

  • Základní adresa https://api.github.com/.
  • Dvě hlavičky potřebné pro práci s rozhraním API GitHubu

CreateClient

Pokaždé CreateClient se volá:

  • Vytvoří se nová instance HttpClient .
  • Volá se akce konfigurace.

Pokud chcete vytvořit pojmenovaného klienta, předejte jeho název do CreateClient:

public class NamedClientModel : PageModel
{
    private readonly IHttpClientFactory _clientFactory;

    public IEnumerable<GitHubPullRequest> PullRequests { get; private set; }

    public bool GetPullRequestsError { get; private set; }

    public bool HasPullRequests => PullRequests.Any();

    public NamedClientModel(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }

    public async Task OnGet()
    {
        var request = new HttpRequestMessage(HttpMethod.Get,
            "repos/dotnet/AspNetCore.Docs/pulls");

        var client = _clientFactory.CreateClient("github");

        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            using var responseStream = await response.Content.ReadAsStreamAsync();
            PullRequests = await JsonSerializer.DeserializeAsync
                    <IEnumerable<GitHubPullRequest>>(responseStream);
        }
        else
        {
            GetPullRequestsError = true;
            PullRequests = Array.Empty<GitHubPullRequest>();
        }
    }
}

V předchozím kódu požadavek nemusí zadávat název hostitele. Kód může předat pouze cestu, protože se používá základní adresa nakonfigurovaná pro klienta.

Typoví klienti

Typoví klienti:

  • Poskytněte stejné možnosti jako pojmenovaní klienti bez nutnosti používat řetězce jako klíče.
  • Poskytuje nápovědu intelliSense a kompilátoru při využívání klientů.
  • Zadejte jedno umístění pro konfiguraci a interakci s určitým HttpClient. Může se například použít jeden typ klienta:
    • Pro jeden back-endový koncový bod.
    • Zapouzdření veškeré logiky, která se zabývá koncovým bodem.
  • Pracujte s DI a můžete je v aplikaci vsunou tam, kde je to potřeba.

Zadaný klient přijímá parametr v jeho konstruktoru HttpClient :

public class GitHubService
{
    public HttpClient Client { get; }

    public GitHubService(HttpClient client)
    {
        client.BaseAddress = new Uri("https://api.github.com/");
        // GitHub API versioning
        client.DefaultRequestHeaders.Add("Accept",
            "application/vnd.github.v3+json");
        // GitHub requires a user-agent
        client.DefaultRequestHeaders.Add("User-Agent",
            "HttpClientFactory-Sample");

        Client = client;
    }

    public async Task<IEnumerable<GitHubIssue>> GetAspNetDocsIssues()
    {
        var response = await Client.GetAsync(
            "/repos/dotnet/AspNetCore.Docs/issues?state=open&sort=created&direction=desc");

        response.EnsureSuccessStatusCode();

        using var responseStream = await response.Content.ReadAsStreamAsync();
        return await JsonSerializer.DeserializeAsync
            <IEnumerable<GitHubIssue>>(responseStream);
    }
}

Pokud chcete zobrazit komentáře ke kódu přeložené do jiných jazyků, než je angličtina, dejte nám vědět v této diskuzi na GitHubu.

V předchozím kódu:

  • Konfigurace se přesune do zadaného klienta.
  • Objekt HttpClient je vystavený jako veřejná vlastnost.

Metody specifické pro rozhraní API je možné vytvořit, které zpřístupňují HttpClient funkce. Například GetAspNetDocsIssues metoda zapouzdřuje kód pro načtení otevřených problémů.

Následující volání AddHttpClientStartup.ConfigureServices kódu pro registraci typové klientské třídy:

services.AddHttpClient<GitHubService>();

Zadaný klient je zaregistrovaný jako přechodný s DI. V předchozím kódu AddHttpClient se zaregistruje GitHubService jako přechodná služba. Tato registrace používá metodu továrny k:

  1. Vytvořte instanci HttpClient.
  2. Vytvoření instance , GitHubServicepředání instance HttpClient do jeho konstruktoru.

Zadaný klient se dá vloženého a spotřebovávat přímo:

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

Konfiguraci pro typového klienta je možné zadat při registraci v Startup.ConfigureServicesnástroji , nikoli v konstruktoru zadaného klienta:

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");
});

Zapouzdření HttpClient lze zapouzdřovat v rámci zadaného klienta. Místo zveřejnění jako vlastnosti definujte metodu, která interně volá HttpClient instanci:

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

V předchozím kódu je uložen HttpClient v privátním poli. Přístup k této HttpClient metodě je určen veřejnou GetRepos metodou.

Vygenerované klienty

IHttpClientFactory lze použít v kombinaci s knihovnami třetích stran, jako je například Refit. Refit je REST knihovna pro .NET. REST Převádí rozhraní API na živá rozhraní. Implementace rozhraní se generuje dynamicky RestServicepomocí , pomocí HttpClient k provedení externích volání HTTP.

Rozhraní a odpověď jsou definovány tak, aby představovaly externí rozhraní API a jeho odpověď:

public interface IHelloClient
{
    [Get("/helloworld")]
    Task<Reply> GetMessageAsync();
}

public class Reply
{
    public string Message { get; set; }
}

Typový klient je možné přidat pomocí nástroje Refit k vygenerování implementace:

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

Definované rozhraní lze v případě potřeby využívat s implementací, kterou poskytuje DI a 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();
    }
}

Vytváření požadavků POST, PUT a DELETE

V předchozích příkladech používají všechny požadavky HTTP příkaz GET HTTP. HttpClient také podporuje další příkazy HTTP, včetně:

  • POST
  • PUT
  • SMAZÁNÍ
  • PATCH

Úplný seznam podporovaných příkazů HTTP najdete v tématu HttpMethod.

Následující příklad ukazuje, jak vytvořit požadavek HTTP POST:

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

V předchozím kódu CreateItemAsync metoda:

  • Serializuje TodoItem parametr on JSon using System.Text.Json. To používá instanci JsonSerializerOptions ke konfiguraci procesu serializace.
  • Vytvoří instanci StringContent pro zabalení serializované JSON pro odesílání v těle požadavku HTTP.
  • Volání PostAsync k odeslání JSobsahu ON na zadanou adresu URL. Toto je relativní adresa URL, která se přidá do HttpClient.BaseAddress.
  • Volání EnsureSuccessStatusCode vyvolání výjimky, pokud stavový kód odpovědi nezjistí úspěch.

HttpClient podporuje také jiné typy obsahu. Příklad: MultipartContent a StreamContent. Úplný seznam podporovaného obsahu najdete v tématu HttpContent.

Následující příklad ukazuje požadavek HTTP PUT:

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

Předchozí kód je velmi podobný příkladu POST. Metoda SaveItemAsync volá PutAsync místo PostAsync.

Následující příklad ukazuje požadavek HTTP DELETE:

public async Task DeleteItemAsync(long itemId)
{
    using var httpResponse =
        await _httpClient.DeleteAsync($"/api/TodoItems/{itemId}");

    httpResponse.EnsureSuccessStatusCode();
}

V předchozím kódu DeleteItemAsync metoda volá DeleteAsync. Vzhledem k tomu, že požadavky HTTP DELETE obvykle neobsahují žádný text, DeleteAsync metoda neposkytuje přetížení, které přijímá instanci HttpContent.

Další informace o použití různých příkazů HTTP s HttpClient, viz HttpClient.

Middleware odchozích požadavků

HttpClient má koncept delegování obslužných rutin, které lze propojit pro odchozí požadavky HTTP. IHttpClientFactory:

  • Zjednodušuje definování obslužných rutin, které se mají použít pro každého pojmenovaného klienta.
  • Podporuje registraci a řetězení více obslužných rutin pro vytvoření kanálu middlewaru odchozích požadavků. Každý z těchto obslužných rutin může provádět práci před a po odchozím požadavku. Tento vzor:
    • Podobá se kanálu příchozího middlewaru v ASP.NET Core.
    • Poskytuje mechanismus pro správu průřezových obav souvisejících s požadavky HTTP, například:
      • Mezipaměti
      • zpracování chyb
      • Serializace
      • protokolování

Vytvoření delegující obslužné rutiny:

  • Odvozovat od DelegatingHandler.
  • Přepsat SendAsync. Před předáním požadavku další obslužné rutině v kanálu spusťte kód:
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);
    }
}

Předchozí kód zkontroluje, jestli je hlavička X-API-KEY v požadavku. Pokud X-API-KEY chybí, BadRequest vrátí se.

Do konfigurace pro jednu obslužnou rutinu lze přidat více než jednu obslužnou rutinu HttpClientMicrosoft.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.

V předchozím kódu je zaregistrovaný ValidateHeaderHandler v DI. Po registraci AddHttpMessageHandler je možné volat předání typu pro obslužnou rutinu.

V pořadí, v jakém se mají spustit, je možné zaregistrovat více obslužných rutin. Každá obslužná rutina zabalí další obslužnou rutinu, dokud konečný HttpClientHandler požadavek nespustí:

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

Použití DI v middlewaru odchozích požadavků

Když IHttpClientFactory vytvoří novou obslužnou rutinu delegování, použije di ke splnění parametrů konstruktoru obslužné rutiny. IHttpClientFactoryvytvoří samostatný obor DI pro každou obslužnou rutinu, což může vést k překvapení chování, když obslužná rutina využívá vymezenou službu.

Představte si například následující rozhraní a jeho implementaci, které představuje úlohu jako operaci s identifikátorem: OperationId

public interface IOperationScoped 
{
    string OperationId { get; }
}

public class OperationScoped : IOperationScoped
{
    public string OperationId { get; } = Guid.NewGuid().ToString()[^4..];
}

Jak napovídá název, IOperationScoped zaregistruje se v DI s využitím vymezené doby života:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<TodoContext>(options =>
        options.UseInMemoryDatabase("TodoItems"));

    services.AddHttpContextAccessor();

    services.AddHttpClient<TodoClient>((sp, httpClient) =>
    {
        var httpRequest = sp.GetRequiredService<IHttpContextAccessor>().HttpContext.Request;

        // For sample purposes, assume TodoClient is used in the context of an incoming request.
        httpClient.BaseAddress = new Uri(UriHelper.BuildAbsolute(httpRequest.Scheme,
                                         httpRequest.Host, httpRequest.PathBase));
        httpClient.Timeout = TimeSpan.FromSeconds(5);
    });

    services.AddScoped<IOperationScoped, OperationScoped>();
    
    services.AddTransient<OperationHandler>();
    services.AddTransient<OperationResponseHandler>();

    services.AddHttpClient("Operation")
        .AddHttpMessageHandler<OperationHandler>()
        .AddHttpMessageHandler<OperationResponseHandler>()
        .SetHandlerLifetime(TimeSpan.FromSeconds(5));

    services.AddControllers();
    services.AddRazorPages();
}

Následující delegování obslužné rutiny využívá a používá IOperationScoped k nastavení X-OPERATION-ID hlavičky pro odchozí požadavek:

public class OperationHandler : DelegatingHandler
{
    private readonly IOperationScoped _operationService;

    public OperationHandler(IOperationScoped operationScoped)
    {
        _operationService = operationScoped;
    }

    protected async override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.Headers.Add("X-OPERATION-ID", _operationService.OperationId);

        return await base.SendAsync(request, cancellationToken);
    }
}

HttpRequestsSample Ve stažení přejděte na /Operation stránku a aktualizujte ji. Hodnota rozsahu požadavku se změní pro každý požadavek, ale hodnota oboru obslužné rutiny se změní pouze každých 5 sekund.

Obslužné rutiny můžou záviset na službách libovolného rozsahu. Služby, na které obslužné rutiny závisejí, jsou uvolněny při odstranění obslužné rutiny.

Ke sdílení stavu jednotlivých požadavků s obslužnými rutinami zpráv použijte jeden z následujících přístupů:

Použití obslužných rutin založených na Polly

IHttpClientFactory se integruje s knihovnou Polly třetích stran. Polly je komplexní knihovna odolnosti a přechodného zpracování chyb pro .NET. Umožňuje vývojářům vyjádřit zásady, jako jsou opakování, jistič, vypršení časového limitu, izolace bulkheadů a náhradní zařízení plynulým a bezpečným způsobem.

Metody rozšíření jsou k dispozici pro povolení použití zásad Polly s nakonfigurovanými HttpClient instancemi. Rozšíření Polly podporují přidávání obslužných rutin založených na Polly do klientů. Polly vyžaduje balíček NuGet Microsoft.Extensions.Http.Polly .

Zpracování přechodných chyb

K chybám obvykle dochází, když jsou externí volání HTTP přechodná. AddTransientHttpErrorPolicy umožňuje definovat zásadu pro zpracování přechodných chyb. Zásady nakonfigurované pomocí AddTransientHttpErrorPolicy zpracování následujících odpovědí:

AddTransientHttpErrorPolicy poskytuje přístup k objektu nakonfigurovaného PolicyBuilder pro zpracování chyb představujících možnou přechodnou chybu:

public void ConfigureServices(IServiceCollection services)
{           
    services.AddHttpClient<UnreliableEndpointCallerService>()
        .AddTransientHttpErrorPolicy(p => 
            p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));

    // Remaining code deleted for brevity.

V předchozím kódu je definována WaitAndRetryAsync zásada. Neúspěšné požadavky se budou opakovat až třikrát se zpožděním 600 ms mezi pokusy.

Dynamické výběr zásad

Metody rozšíření jsou k dispozici pro přidání obslužných rutin založených na Polly, AddPolicyHandlernapříklad . AddPolicyHandler Následující přetížení zkontroluje požadavek a rozhodne, které zásady se mají použít:

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);

Pokud je odchozím požadavkem v předchozím kódu HTTP GET, použije se časový limit 10 sekund. Pro jakoukoli jinou metodu HTTP se použije časový limit 30 sekund.

Přidání více obslužných rutin Polly

Vnoření zásad Polly je běžné:

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

V předchozím příkladu:

  • Přidají se dva obslužné rutiny.
  • První obslužná rutina používá AddTransientHttpErrorPolicy k přidání zásady opakování. Neúspěšné požadavky se budou opakovat až třikrát.
  • Druhé AddTransientHttpErrorPolicy volání přidá zásadu jističe. Další externí požadavky se zablokují po dobu 30 sekund, pokud dojde k 5 neúspěšným pokusům. Zásady jističe jsou stavové. Všechna volání prostřednictvím tohoto klienta sdílejí stejný stav okruhu.

Přidání zásad z registru Polly

Přístup ke správě pravidelně používaných zásad je definovat je jednou a zaregistrovat je v PolicyRegistryprogramu .

V následujícím kódu:

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.

Další informace o IHttpClientFactory integraci a integraci Polly najdete na wikiwebu Polly.

Správa klienta HttpClient a životnosti

Nová HttpClient instance se vrátí pokaždé, když CreateClient je volána na IHttpClientFactory. Vytvoří se HttpMessageHandler pro pojmenovaného klienta. Továrna spravuje životnosti HttpMessageHandler instancí.

IHttpClientFactory vytváří fondy HttpMessageHandler instancí vytvořených továrnou, aby se snížila spotřeba prostředků. Pokud HttpMessageHandler jeho životnost nevypršela, může být instance z fondu HttpClient znovu použita.

Sdružování obslužných rutin je žádoucí, protože každá obslužná rutina obvykle spravuje vlastní základní připojení HTTP. Vytvoření více obslužných rutin, než je potřeba, může vést ke zpoždění připojení. Některé obslužné rutiny také trvale udržují připojení otevřená, což může obslužné rutině zabránit v reakci na změny DNS (Domain Name System).

Výchozí životnost obslužné rutiny je dvě minuty. Výchozí hodnotu lze přepsat na základě pojmenovaného klienta:

public void ConfigureServices(IServiceCollection services)
{           
    services.AddHttpClient("extendedhandlerlifetime")
        .SetHandlerLifetime(TimeSpan.FromMinutes(5));

    // Remaining code deleted for brevity.

HttpClient Instance lze obecně považovat za objekty .NET, které nevyžadují odstranění. Odstranění zruší odchozí požadavky a zaručuje, že danou HttpClient instanci nelze po volání Disposepoužít . IHttpClientFactory sleduje a odstraňuje prostředky používané instancemi HttpClient .

Udržování jedné HttpClient instance naživu po dlouhou dobu je běžný vzor používaný před vznikem IHttpClientFactory. Tento model se po migraci na IHttpClientFactory.

Alternativy k IHttpClientFactory

Použití IHttpClientFactory v aplikaci s podporou DI se vyhne:

  • Problémy s vyčerpáním prostředků sdružováním HttpMessageHandler instancí
  • Zastaralé problémy DNS cyklickými HttpMessageHandler instancemi v pravidelných intervalech.

Existují alternativní způsoby řešení předchozích problémů pomocí dlouhodobé SocketsHttpHandler instance.

  • Vytvořte instanci SocketsHttpHandler , kdy se aplikace spustí a použije ji pro životnost aplikace.
  • Nakonfigurujte PooledConnectionLifetime odpovídající hodnotu na základě času aktualizace DNS.
  • Podle potřeby vytvořte HttpClient instance.new HttpClient(handler, disposeHandler: false)

Předchozí přístupy řeší problémy správy prostředků, které IHttpClientFactory řeší podobným způsobem.

  • Sdílené SocketsHttpHandler připojení mezi HttpClient instancemi. Toto sdílení zabraňuje vyčerpání soketů.
  • Cykly SocketsHttpHandler připojení podle toho, aby nedocházelo k PooledConnectionLifetime zastaralým problémům s DNS.

CookieS

Instance ve HttpMessageHandler fondu mají za CookieContainer následek sdílení objektů. CookieContainer Neočekávané sdílení objektů často vede k nesprávnému kódu. U aplikací, které vyžadují cookie, zvažte jednu z těchto možností:

  • Zakázání automatického cookie zpracování
  • Vyhnout IHttpClientFactory

Volání ConfigurePrimaryHttpMessageHandler , které zakáže automatické cookie zpracování:

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

Protokolování

Klienti vytvořená prostřednictvím IHttpClientFactory zpráv protokolu záznamů pro všechny požadavky. Povolte odpovídající úroveň informací v konfiguraci protokolování, abyste viděli výchozí zprávy protokolu. Další protokolování, například protokolování hlaviček požadavků, je zahrnuto pouze na úrovni trasování.

Kategorie protokolu používaná pro každého klienta obsahuje název klienta. Klient s názvem MyNamedClient například protokoluje zprávy s kategorií System.Net.Http.HttpClient.MyNamedClient. Logická obslužná rutina. Zprávy s příponou LogicalHandler se vyskytují mimo kanál obslužné rutiny požadavku. Na požadavku se zprávy protokolují předtím, než je zpracují ostatní obslužné rutiny v kanálu. V odpovědi se zprávy zaprotokolují po přijetí odpovědi jinými obslužnými rutinami kanálu.

Protokolování také probíhá v kanálu obslužné rutiny požadavku. V příkladu MyNamedClient se tyto zprávy protokolují s kategorií protokolu System.Net.Http.HttpClient.MyNamedClient. ClientHandler. U požadavku k tomu dochází po spuštění všech ostatních obslužných rutin a bezprostředně před odesláním požadavku. Toto protokolování v odpovědi zahrnuje stav odpovědi před tím, než projde zpět kanálem obslužné rutiny.

Povolení protokolování mimo kanál a uvnitř kanálu umožňuje kontrolu změn provedených jinými obslužnými rutinami kanálu. To může zahrnovat změny hlaviček požadavků nebo stavového kódu odpovědi.

Zahrnutí názvu klienta do kategorie protokolu umožňuje filtrování protokolů pro konkrétní pojmenované klienty.

Konfigurace obslužné rutiny HttpMessage

Může být nutné řídit konfiguraci vnitřního HttpMessageHandler prostředí používaného klientem.

Vrátí IHttpClientBuilder se při přidávání pojmenovaných nebo zadaných klientů. Metodu ConfigurePrimaryHttpMessageHandler rozšíření lze použít k definování delegáta. Delegát se používá k vytvoření a konfiguraci primárního HttpMessageHandler klienta, který tento klient používá:

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

    // Remaining code deleted for brevity.

Použití IHttpClientFactory v konzolové aplikaci

V aplikaci konzoly přidejte do projektu následující odkazy na balíčky:

V následujícím příkladu:

  • IHttpClientFactory je zaregistrovaný v kontejneru služby obecného hostitele .
  • MyService vytvoří instanci klientské továrny ze služby, která slouží k vytvoření objektu HttpClient. HttpClient slouží k načtení webové stránky.
  • Main vytvoří obor pro spuštění metody služby GetPage a zápis prvních 500 znaků obsahu webové stránky do konzoly.
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
class Program
{
    static async Task<int> Main(string[] args)
    {
        var builder = new HostBuilder()
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHttpClient();
                services.AddTransient<IMyService, MyService>();
            }).UseConsoleLifetime();

        var host = builder.Build();

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

            Console.WriteLine(pageContent.Substring(0, 500));
        }
        catch (Exception ex)
        {
            var logger = host.Services.GetRequiredService<ILogger<Program>>();

            logger.LogError(ex, "An error occurred.");
        }

        return 0;
    }

    public interface IMyService
    {
        Task<string> GetPage();
    }

    public class MyService : IMyService
    {
        private readonly IHttpClientFactory _clientFactory;

        public MyService(IHttpClientFactory clientFactory)
        {
            _clientFactory = clientFactory;
        }

        public async Task<string> GetPage()
        {
            // Content from BBC One: Dr. Who website (©BBC)
            var request = new HttpRequestMessage(HttpMethod.Get,
                "https://www.bbc.co.uk/programmes/b006q2x0");
            var client = _clientFactory.CreateClient();
            var response = await client.SendAsync(request);

            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadAsStringAsync();
            }
            else
            {
                return $"StatusCode: {response.StatusCode}";
            }
        }
    }
}

Middleware šíření hlaviček

Šíření hlaviček je middleware ASP.NET Core, který rozšíří hlavičky HTTP z příchozího požadavku na odchozí požadavky klienta HTTP. Použití šíření hlaviček:

  • Odkaz na balíček Microsoft.AspNetCore.HeaderPropagation .

  • Nakonfigurujte middleware a HttpClient v 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();
        });
    }
    
  • Klient zahrnuje nakonfigurované hlavičky u odchozích požadavků:

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

Další prostředky

Glenn Condron, Ryan Nowak a Steve Gordon

Můžete IHttpClientFactory ho zaregistrovat a použít ke konfiguraci a vytvoření HttpClient instancí v aplikaci. Nabízí následující výhody:

  • Poskytuje centrální umístění pro pojmenování a konfiguraci logických instancí HttpClient. Klient GitHubu je například možné zaregistrovat a nakonfigurovat pro přístup k GitHubu. Výchozího klienta je možné zaregistrovat pro jiné účely.
  • Kodifikuje koncept odchozího middlewaru prostřednictvím delegování obslužných rutin a HttpClient poskytuje rozšíření pro middleware založený na Polly, aby ho mohli využít.
  • Spravuje sdružování a životnost základních HttpClientMessageHandler instancí, aby se zabránilo běžným problémům s DNS, ke kterým dochází při ruční správě HttpClient životnosti.
  • Přidá konfigurovatelné protokolování (prostřednictvím ILogger) pro všechny požadavky odeslané prostřednictvím klientů vytvořených továrnou.

Zobrazení nebo stažení ukázkového kódu (postup stažení)

Požadavky

Projekty, které cílí na rozhraní .NET Framework, vyžadují instalaci balíčku NuGet Microsoft.Extensions.Http . Projekty, které cílí na .NET Core a odkazují na Microsoft.AspNetCore.App metabalíč , už balíček obsahují Microsoft.Extensions.Http .

Spotřeby

V aplikaci můžete použít několik způsobů IHttpClientFactory :

Nikdo z nich není přísně nadřazený jinému. Nejlepší přístup závisí na omezeních aplikace.

Základní použití

Lze IHttpClientFactory zaregistrovat voláním AddHttpClient rozšiřující metody na IServiceCollection, uvnitř Startup.ConfigureServices metody.

services.AddHttpClient();

Po registraci může kód přijmout IHttpClientFactory jakékoli služby, které je možné do injektáže závislostí (DI) vloženého. Dá IHttpClientFactory se použít k vytvoření 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/dotnet/AspNetCore.Docs/branches");
        request.Headers.Add("Accept", "application/vnd.github.v3+json");
        request.Headers.Add("User-Agent", "HttpClientFactory-Sample");

        var client = _clientFactory.CreateClient();

        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            Branches = await response.Content
                .ReadAsAsync<IEnumerable<GitHubBranch>>();
        }
        else
        {
            GetBranchesError = true;
            Branches = Array.Empty<GitHubBranch>();
        }                               
    }
}

Použití IHttpClientFactory tímto způsobem je dobrým způsobem, jak refaktorovat existující aplikaci. Nemá žádný vliv na způsob HttpClient použití. V místech, kde HttpClient jsou instance aktuálně vytvořeny, nahraďte tyto výskyty voláním CreateClient.

Pojmenovaní klienti

Pokud aplikace vyžaduje mnoho různých použití HttpClient, každý s jinou konfigurací, je možnost použít pojmenované klienty. Konfigurace pro pojmenovaný HttpClient lze zadat během registrace v 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");
});

V předchozím kódu AddHttpClient se volá a poskytuje název githubu. Tento klient má použitou výchozí konfiguraci – konkrétně základní adresu a dvě hlavičky potřebné pro práci s rozhraním API GitHubu.

Při CreateClient každém zavolání se vytvoří nová instance HttpClient a zavolá se akce konfigurace.

Pokud chcete použít pojmenovaného klienta, lze řetězcový parametr předat .CreateClient Zadejte název klienta, který se má vytvořit:

public class NamedClientModel : PageModel
{
    private readonly IHttpClientFactory _clientFactory;

    public IEnumerable<GitHubPullRequest> PullRequests { get; private set; }

    public bool GetPullRequestsError { get; private set; }

    public bool HasPullRequests => PullRequests.Any();

    public NamedClientModel(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }

    public async Task OnGet()
    {
        var request = new HttpRequestMessage(HttpMethod.Get, 
            "repos/dotnet/AspNetCore.Docs/pulls");

        var client = _clientFactory.CreateClient("github");

        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            PullRequests = await response.Content
                .ReadAsAsync<IEnumerable<GitHubPullRequest>>();
        }
        else
        {
            GetPullRequestsError = true;
            PullRequests = Array.Empty<GitHubPullRequest>();
        }
    }
}

V předchozím kódu požadavek nemusí zadávat název hostitele. Může předat pouze cestu, protože se používá základní adresa nakonfigurovaná pro klienta.

Typoví klienti

Typoví klienti:

  • Poskytněte stejné možnosti jako pojmenovaní klienti bez nutnosti používat řetězce jako klíče.
  • Poskytuje nápovědu intelliSense a kompilátoru při využívání klientů.
  • Zadejte jedno umístění pro konfiguraci a interakci s určitým HttpClient. Pro jeden back-endový koncový bod může být například použit jeden typ klienta a zapouzdření veškeré logiky, která se týká daného koncového bodu.
  • Pracujte s DI a můžete ho v aplikaci vsunou tam, kde je to potřeba.

Zadaný klient přijímá parametr v jeho konstruktoru HttpClient :

public class GitHubService
{
    public HttpClient Client { get; }

    public GitHubService(HttpClient client)
    {
        client.BaseAddress = new Uri("https://api.github.com/");
        // GitHub API versioning
        client.DefaultRequestHeaders.Add("Accept", 
            "application/vnd.github.v3+json");
        // GitHub requires a user-agent
        client.DefaultRequestHeaders.Add("User-Agent", 
            "HttpClientFactory-Sample");

        Client = client;
    }

    public async Task<IEnumerable<GitHubIssue>> GetAspNetDocsIssues()
    {
        var response = await Client.GetAsync(
            "/repos/dotnet/AspNetCore.Docs/issues?state=open&sort=created&direction=desc");

        response.EnsureSuccessStatusCode();

        var result = await response.Content
            .ReadAsAsync<IEnumerable<GitHubIssue>>();

        return result;
    }
}

V předchozím kódu se konfigurace přesune do zadaného klienta. Objekt HttpClient je vystavený jako veřejná vlastnost. Je možné definovat metody specifické pro rozhraní API, které zpřístupňují HttpClient funkce. Metoda GetAspNetDocsIssues zapouzdřuje kód potřebný k dotazování a parsování nejnovějších otevřených problémů z úložiště GitHub.

Chcete-li zaregistrovat typ klienta, lze použít obecnou AddHttpClient metodu rozšíření v rámci Startup.ConfigureServices, určující typ třídy klienta:

services.AddHttpClient<GitHubService>();

Zadaný klient je zaregistrovaný jako přechodný s DI. Zadaný klient se dá vloženého a spotřebovávat přímo:

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

V případě potřeby lze při registraci Startup.ConfigureServiceszadat konfiguraci pro typového klienta, nikoli v konstruktoru zadaného klienta:

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");
});

Je možné zcela zapouzdřovat HttpClient v rámci zadaného klienta. Místo zveřejnění jako vlastnosti lze poskytnout veřejné metody, které interně volají HttpClient instanci.

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;
    }
}

V předchozím kódu HttpClient se uloží jako soukromé pole. Veškerý přístup k provádění externích volání prochází metodou GetRepos .

Vygenerované klienty

IHttpClientFactory lze použít v kombinaci s jinými knihovnami třetích stran, jako je například Refit. Refit je REST knihovna pro .NET. REST Převádí rozhraní API na živá rozhraní. Implementace rozhraní se generuje dynamicky RestServicepomocí , pomocí HttpClient k provedení externích volání HTTP.

Rozhraní a odpověď jsou definovány tak, aby představovaly externí rozhraní API a jeho odpověď:

public interface IHelloClient
{
    [Get("/helloworld")]
    Task<Reply> GetMessageAsync();
}

public class Reply
{
    public string Message { get; set; }
}

Typový klient je možné přidat pomocí nástroje Refit k vygenerování implementace:

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

Definované rozhraní lze v případě potřeby využívat s implementací, kterou poskytuje DI a 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 odchozích požadavků

HttpClient již má koncept delegování obslužných rutin, které lze propojit pro odchozí požadavky HTTP. Usnadňuje IHttpClientFactory definování obslužných rutin, které se mají použít pro každého pojmenovaného klienta. Podporuje registraci a řetězení více obslužných rutin pro vytvoření kanálu middlewaru odchozích požadavků. Každý z těchto obslužných rutin může provádět práci před a po odchozím požadavku. Tento model je podobný kanálu příchozího middlewaru v ASP.NET Core. Tento model poskytuje mechanismus pro správu průřezových problémů souvisejících s požadavky HTTP, včetně ukládání do mezipaměti, zpracování chyb, serializace a protokolování.

Chcete-li vytvořit obslužnou rutinu, definujte třídu odvozenou z DelegatingHandler. Přepište metodu spuštění SendAsync kódu před předáním požadavku do další obslužné rutiny v kanálu:

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

Předchozí kód definuje základní obslužnou rutinu. Zkontroluje, jestli X-API-KEY je v požadavku zahrnutá hlavička. Pokud hlavička chybí, může se vyhnout volání HTTP a vrátit vhodnou odpověď.

Během registrace je možné do konfigurace pro objekt HttpClientpřidat jeden nebo více obslužných rutin . Tento úkol se provádí prostřednictvím rozšiřujících metod v objektu 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>();

V předchozím kódu je zaregistrovaný ValidateHeaderHandler v DI. Obslužná rutina musí být registrována v distanci jako přechodná služba, nikdy není vymezena. Pokud je obslužná rutina zaregistrovaná jako služba s vymezeným oborem a všechny služby, na které obslužná rutina závisí, jsou uvolnitelné:

  • Služby obslužné rutiny je možné likvidovat předtím, než obslužná rutina přestane být vymezena.
  • Uvolněné služby obslužné rutiny způsobí selhání obslužné rutiny.

Po registraci AddHttpMessageHandler lze volat předání typu obslužné rutiny.

V pořadí, v jakém se mají spustit, je možné zaregistrovat více obslužných rutin. Každá obslužná rutina zabalí další obslužnou rutinu, dokud konečný HttpClientHandler požadavek nespustí:

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

Ke sdílení stavu jednotlivých požadavků s obslužnými rutinami zpráv použijte jeden z následujících přístupů:

  • Předání dat do obslužné rutiny pomocí HttpRequestMessage.Properties.
  • Slouží IHttpContextAccessor k přístupu k aktuálnímu požadavku.
  • Vytvořte vlastní AsyncLocal objekt úložiště pro předávání dat.

Použití obslužných rutin založených na Polly

IHttpClientFactory integruje se s oblíbenou knihovnou třetích stran s názvem Polly. Polly je komplexní knihovna odolnosti a přechodného zpracování chyb pro .NET. Umožňuje vývojářům vyjádřit zásady, jako jsou opakování, jistič, vypršení časového limitu, izolace bulkheadů a náhradní zařízení plynulým a bezpečným způsobem.

Metody rozšíření jsou k dispozici pro povolení použití zásad Polly s nakonfigurovanými HttpClient instancemi. Rozšíření Polly:

Zpracování přechodných chyb

K nejčastějším chybám dochází v případě, že externí volání HTTP jsou přechodná. Je zahrnuta pohodlná metoda AddTransientHttpErrorPolicy rozšíření, která umožňuje definovat zásadu pro zpracování přechodných chyb. Zásady nakonfigurované pomocí tohoto popisovače HttpRequestExceptionmetody rozšíření, odpovědi HTTP 5xx a odpovědi HTTP 408.

Rozšíření AddTransientHttpErrorPolicy lze použít v rámci Startup.ConfigureServices. Rozšíření poskytuje přístup k objektu nakonfigurovaného PolicyBuilder pro zpracování chyb představující možnou přechodnou chybu:

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

V předchozím kódu je definována WaitAndRetryAsync zásada. Neúspěšné požadavky se budou opakovat až třikrát se zpožděním 600 ms mezi pokusy.

Dynamické výběr zásad

Existují další metody rozšíření, které lze použít k přidání obslužných rutin založených na Polly. Jedním z takových rozšíření je AddPolicyHandler, který má více přetížení. Jedno přetížení umožňuje, aby se požadavek kontroloval při definování zásad, které se mají použít:

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);

Pokud je odchozím požadavkem v předchozím kódu HTTP GET, použije se časový limit 10 sekund. Pro jakoukoli jinou metodu HTTP se použije časový limit 30 sekund.

Přidání více obslužných rutin Polly

Vnoření zásad Polly pro zajištění vylepšených funkcí je běžné:

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

V předchozím příkladu se přidají dva obslužné rutiny. První rozšíření použije AddTransientHttpErrorPolicy k přidání zásady opakování. Neúspěšné požadavky se budou opakovat až třikrát. Druhé volání, které AddTransientHttpErrorPolicy přidá zásadu jističe. Pokud dojde k pěti neúspěšným pokusům, zablokují se další externí požadavky po dobu 30 sekund. Zásady jističe jsou stavové. Všechna volání prostřednictvím tohoto klienta sdílejí stejný stav okruhu.

Přidání zásad z registru Polly

Přístup ke správě pravidelně používaných zásad je definovat je jednou a zaregistrovat je v PolicyRegistryprogramu . Poskytuje se metoda rozšíření, která umožňuje přidání obslužné rutiny pomocí zásady z registru:

var registry = services.AddPolicyRegistry();

registry.Add("regular", timeout);
registry.Add("long", longTimeout);

services.AddHttpClient("regulartimeouthandler")
    .AddPolicyHandlerFromRegistry("regular");

V předchozím kódu jsou při přidání do PolicyRegistryServiceCollectionsouboru zaregistrovány dvě zásady . Pokud chcete použít zásadu z registru, AddPolicyHandlerFromRegistry použije se metoda a předá název zásady, která se má použít.

Další informace o IHttpClientFactory integraci Polly najdete na wikiwebu Polly.

Správa klienta HttpClient a životnosti

Nová HttpClient instance se vrátí pokaždé, když CreateClient je volána na IHttpClientFactory. Existuje jeden pojmenovaný HttpMessageHandler klient. Továrna spravuje životnosti HttpMessageHandler instancí.

IHttpClientFactory vytváří fondy HttpMessageHandler instancí vytvořených továrnou, aby se snížila spotřeba prostředků. Pokud HttpMessageHandler jeho životnost nevypršela, může být instance z fondu HttpClient znovu použita.

Sdružování obslužných rutin je žádoucí, protože každá obslužná rutina obvykle spravuje vlastní základní připojení HTTP. Vytvoření více obslužných rutin, než je potřeba, může vést ke zpoždění připojení. Některé obslužné rutiny také trvale udržují připojení otevřená, což může obslužné rutině zabránit v reakci na změny DNS.

Výchozí životnost obslužné rutiny je dvě minuty. Výchozí hodnotu lze přepsat na základě pojmenovaného klienta. Pokud ho IHttpClientBuilder chcete přepsat, zavolejte SetHandlerLifetime vrácené při vytváření klienta:

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

Vyřazení klienta se nevyžaduje. Odstranění zruší odchozí požadavky a zaručuje, že danou HttpClient instanci nelze po volání Disposepoužít . IHttpClientFactory sleduje a odstraňuje prostředky používané instancemi HttpClient . Instance HttpClient lze obecně považovat za objekty .NET, které nevyžadují odstranění.

Udržování jedné HttpClient instance naživu po dlouhou dobu je běžný vzor používaný před vznikem IHttpClientFactory. Tento model se po migraci na IHttpClientFactory.

Alternativy k IHttpClientFactory

Použití IHttpClientFactory v aplikaci s podporou DI se vyhne:

  • Problémy s vyčerpáním prostředků sdružováním HttpMessageHandler instancí
  • Zastaralé problémy DNS cyklickými HttpMessageHandler instancemi v pravidelných intervalech.

Existují alternativní způsoby řešení předchozích problémů pomocí dlouhodobé SocketsHttpHandler instance.

  • Vytvořte instanci SocketsHttpHandler , kdy se aplikace spustí a použije ji pro životnost aplikace.
  • Nakonfigurujte PooledConnectionLifetime odpovídající hodnotu na základě času aktualizace DNS.
  • Podle potřeby vytvořte HttpClient instance.new HttpClient(handler, disposeHandler: false)

Předchozí přístupy řeší problémy správy prostředků, které IHttpClientFactory řeší podobným způsobem.

  • Sdílené SocketsHttpHandler připojení mezi HttpClient instancemi. Toto sdílení zabraňuje vyčerpání soketů.
  • Cykly SocketsHttpHandler připojení podle toho, aby nedocházelo k PooledConnectionLifetime zastaralým problémům s DNS.

CookieS

Instance ve HttpMessageHandler fondu mají za CookieContainer následek sdílení objektů. CookieContainer Neočekávané sdílení objektů často vede k nesprávnému kódu. U aplikací, které vyžadují cookie, zvažte jednu z těchto možností:

  • Zakázání automatického cookie zpracování
  • Vyhnout IHttpClientFactory

Volání ConfigurePrimaryHttpMessageHandler , které zakáže automatické cookie zpracování:

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

Protokolování

Klienti vytvořená prostřednictvím IHttpClientFactory zpráv protokolu záznamů pro všechny požadavky. Povolte odpovídající úroveň informací v konfiguraci protokolování, abyste viděli výchozí zprávy protokolu. Další protokolování, například protokolování hlaviček požadavků, je zahrnuto pouze na úrovni trasování.

Kategorie protokolu používaná pro každého klienta obsahuje název klienta. Klient s názvem MyNamedClient například protokoluje zprávy s kategorií System.Net.Http.HttpClient.MyNamedClient.LogicalHandler. Zprávy s příponou LogicalHandler se vyskytují mimo kanál obslužné rutiny požadavku. Na požadavku se zprávy protokolují předtím, než je zpracují ostatní obslužné rutiny v kanálu. V odpovědi se zprávy zaprotokolují po přijetí odpovědi jinými obslužnými rutinami kanálu.

Protokolování také probíhá v kanálu obslužné rutiny požadavku. V příkladu MyNamedClient jsou tyto zprávy protokolovány do kategorie System.Net.Http.HttpClient.MyNamedClient.ClientHandlerprotokolu . U požadavku k tomu dochází po spuštění všech ostatních obslužných rutin a bezprostředně před odesláním požadavku v síti. Toto protokolování v odpovědi zahrnuje stav odpovědi před tím, než projde zpět kanálem obslužné rutiny.

Povolení protokolování mimo kanál a uvnitř kanálu umožňuje kontrolu změn provedených jinými obslužnými rutinami kanálu. To může zahrnovat změny hlaviček požadavků, například nebo stavového kódu odpovědi.

Zahrnutí názvu klienta do kategorie protokolu umožňuje filtrování protokolů pro konkrétní pojmenované klienty, pokud je to potřeba.

Konfigurace obslužné rutiny HttpMessage

Může být nutné řídit konfiguraci vnitřního HttpMessageHandler prostředí používaného klientem.

Vrátí IHttpClientBuilder se při přidávání pojmenovaných nebo zadaných klientů. Metodu ConfigurePrimaryHttpMessageHandler rozšíření lze použít k definování delegáta. Delegát se používá k vytvoření a konfiguraci primárního HttpMessageHandler klienta, který tento klient používá:

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

Použití IHttpClientFactory v konzolové aplikaci

V aplikaci konzoly přidejte do projektu následující odkazy na balíčky:

V následujícím příkladu:

  • IHttpClientFactory je zaregistrovaný v kontejneru služby obecného hostitele .
  • MyService vytvoří instanci klientské továrny ze služby, která slouží k vytvoření objektu HttpClient. HttpClient slouží k načtení webové stránky.
  • Metoda služby GetPage se spustí tak, aby do konzoly zapisovala prvních 500 znaků obsahu webové stránky. Další informace o volání služeb z Program.Maintématu Injektáž závislostí v ASP.NET Core.
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
class Program
{
    static async Task<int> Main(string[] args)
    {
        var builder = new HostBuilder()
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHttpClient();
                services.AddTransient<IMyService, MyService>();
            }).UseConsoleLifetime();

        var host = builder.Build();

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

            Console.WriteLine(pageContent.Substring(0, 500));
        }
        catch (Exception ex)
        {
            var logger = host.Services.GetRequiredService<ILogger<Program>>();

            logger.LogError(ex, "An error occurred.");
        }

        return 0;
    }

    public interface IMyService
    {
        Task<string> GetPage();
    }

    public class MyService : IMyService
    {
        private readonly IHttpClientFactory _clientFactory;

        public MyService(IHttpClientFactory clientFactory)
        {
            _clientFactory = clientFactory;
        }

        public async Task<string> GetPage()
        {
            // Content from BBC One: Dr. Who website (©BBC)
            var request = new HttpRequestMessage(HttpMethod.Get,
                "https://www.bbc.co.uk/programmes/b006q2x0");
            var client = _clientFactory.CreateClient();
            var response = await client.SendAsync(request);

            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadAsStringAsync();
            }
            else
            {
                return $"StatusCode: {response.StatusCode}";
            }
        }
    }
}

Middleware šíření hlaviček

Šíření hlaviček je middleware podporovaný komunitou pro šíření hlaviček HTTP z příchozího požadavku na odchozí požadavky klienta HTTP. Použití šíření hlaviček:

  • Odkazujte na port podporovaný komunitou balíčku HeaderPropagation. ASP.NET Core 3.1 a novější podporuje Microsoft.AspNetCore.HeaderPropagation.

  • Nakonfigurujte middleware a HttpClient v 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();
    }
    
  • Klient zahrnuje nakonfigurované hlavičky u odchozích požadavků:

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

Další prostředky