Vytváření požadavků HTTP pomocí IHttpClientFactory v ASP.NET Core
Poznámka:
Toto není nejnovější verze tohoto článku. Aktuální verzi najdete v tomto článku ve verzi .NET 9.
Upozorňující
Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v zásadách podpory .NET a .NET Core. Aktuální verzi najdete v tomto článku ve verzi .NET 9.
Důležité
Tyto informace se týkají předběžného vydání produktu, který může být podstatně změněn před komerčním vydáním. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.
Aktuální verzi najdete v tomto článku ve verzi .NET 9.
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 vHttpClient
. - 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 slouží System.Text.Json k deserializaci obsahu JSON 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
HttpClient
z 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í AddHttpClient Program.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:
- Vytvořte instanci
HttpClient
. - Vytvoření instance ,
GitHubService
předání instanceHttpClient
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.cs
ná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 HttpClient
objektu:
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
- DELETE
- 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 do FORMÁTU JSON pomocíSystem.Text.Json
. - Vytvoří instanci pro zabalení serializovaného StringContent JSON pro odeslání v těle požadavku HTTP.
- Volání PostAsync pro odeslání obsahu JSON 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:
- ukrývání
- 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 HttpClient
Microsoft.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. IHttpClientFactory
vytvoří 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ů:
- Předání dat do obslužné rutiny pomocí HttpRequestMessage.Options.
- Slouží IHttpContextAccessor k přístupu k aktuálnímu požadavku.
- Vytvořte vlastní AsyncLocal<T> objekt úložiště pro předávání dat.
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í:
- HttpRequestException
- HTTP 5xx
- HTTP 408
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 PolicyRegistry
programu . 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
aLong
do 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í meziHttpClient
instancemi. Toto sdílení zabraňuje vyčerpání soketů. - Cykly
SocketsHttpHandler
připojení podle toho, aby nedocházelo kPooledConnectionLifetime
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
});
Soubory cookie
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í soubory 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 instanciIHttpClientFactory
.GitHubService
používáIHttpClientFactory
k vytvoření instance , kterouHttpClient
použí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
vProgram.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ší materiály
- Zobrazení nebo stažení ukázkového kódu (postup stažení)
- Použití HttpClientFactory k implementaci odolných požadavků HTTP
- Implementace opakování volání HTTP s exponenciálním zpochybováním pomocí zásad HttpClientFactory a Polly
- Implementace systému jističe
- Jak serializovat a deserializovat JSON v .NET
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 vHttpClient
. - 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 slouží System.Text.Json k deserializaci obsahu JSON 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
HttpClient
z 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í AddHttpClient Startup.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:
- Vytvořte instanci
HttpClient
. - Vytvoření instance ,
GitHubService
předání instanceHttpClient
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.ConfigureServices
ná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 RestService
pomocí , 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
- DELETE
- 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 do FORMÁTU JSON pomocíSystem.Text.Json
. To používá instanci JsonSerializerOptions ke konfiguraci procesu serializace. - Vytvoří instanci pro zabalení serializovaného StringContent JSON pro odeslání v těle požadavku HTTP.
- Volání PostAsync pro odeslání obsahu JSON 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:
- ukrývání
- 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 HttpClient
Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandler:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ValidateHeaderHandler>();
services.AddHttpClient("externalservice", c =>
{
// Assume this is an "external" service which requires an API KEY
c.BaseAddress = new Uri("https://localhost:5001/");
})
.AddHttpMessageHandler<ValidateHeaderHandler>();
// Remaining code deleted for brevity.
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. IHttpClientFactory
vytvoří 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ů:
- Předání dat do obslužné rutiny pomocí HttpRequestMessage.Options.
- Slouží IHttpContextAccessor k přístupu k aktuálnímu požadavku.
- Vytvořte vlastní AsyncLocal<T> objekt úložiště pro předávání dat.
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í:
- HttpRequestException
- HTTP 5xx
- HTTP 408
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 PolicyRegistry
programu .
V následujícím kódu:
- Přidají se zásady "regular" a "long".
- AddPolicyHandlerFromRegistry přidá z registru zásady "regular" a "long".
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í meziHttpClient
instancemi. Toto sdílení zabraňuje vyčerpání soketů. - Cykly
SocketsHttpHandler
připojení podle toho, aby nedocházelo kPooledConnectionLifetime
zastaralým problémům s DNS.
Soubory cookie
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í soubory 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í objektuHttpClient
.HttpClient
slouží k načtení webové stránky.Main
vytvoří obor pro spuštění metody službyGetPage
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:
Nakonfigurujte middleware a
HttpClient
vStartup
: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ší materiály
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 vHttpClient
. - 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 slouží System.Text.Json k deserializaci obsahu JSON 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
HttpClient
z 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í AddHttpClient Startup.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:
- Vytvořte instanci
HttpClient
. - Vytvoření instance ,
GitHubService
předání instanceHttpClient
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.ConfigureServices
ná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 RestService
pomocí , 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
- DELETE
- 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 do FORMÁTU JSON pomocíSystem.Text.Json
. To používá instanci JsonSerializerOptions ke konfiguraci procesu serializace. - Vytvoří instanci pro zabalení serializovaného StringContent JSON pro odeslání v těle požadavku HTTP.
- Volání PostAsync pro odeslání obsahu JSON 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:
- ukrývání
- 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 HttpClient
Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandler:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ValidateHeaderHandler>();
services.AddHttpClient("externalservice", c =>
{
// Assume this is an "external" service which requires an API KEY
c.BaseAddress = new Uri("https://localhost:5001/");
})
.AddHttpMessageHandler<ValidateHeaderHandler>();
// Remaining code deleted for brevity.
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. IHttpClientFactory
vytvoří 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ů:
- 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<T> objekt úložiště pro předávání dat.
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í:
- HttpRequestException
- HTTP 5xx
- HTTP 408
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 PolicyRegistry
programu .
V následujícím kódu:
- Přidají se zásady "regular" a "long".
- AddPolicyHandlerFromRegistry přidá z registru zásady "regular" a "long".
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í meziHttpClient
instancemi. Toto sdílení zabraňuje vyčerpání soketů. - Cykly
SocketsHttpHandler
připojení podle toho, aby nedocházelo kPooledConnectionLifetime
zastaralým problémům s DNS.
Soubory cookie
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í soubory 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í objektuHttpClient
.HttpClient
slouží k načtení webové stránky.Main
vytvoří obor pro spuštění metody službyGetPage
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:
Nakonfigurujte middleware a
HttpClient
vStartup
: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ší materiály
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.ConfigureServices
zadat 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 RestService
pomocí , 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 HttpClient
př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:
- Podpora přidávání obslužných rutin založených na Polly do klientů
- Lze použít po instalaci balíčku NuGet Microsoft.Extensions.Http.Polly . Balíček není součástí sdílené architektury ASP.NET Core.
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 HttpRequestException
metody 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 PolicyRegistry
programu . 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 PolicyRegistry
ServiceCollection
souboru 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í meziHttpClient
instancemi. Toto sdílení zabraňuje vyčerpání soketů. - Cykly
SocketsHttpHandler
připojení podle toho, aby nedocházelo kPooledConnectionLifetime
zastaralým problémům s DNS.
Soubory cookie
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í soubory 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.ClientHandler
protokolu . 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í objektuHttpClient
.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 zProgram.Main
té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
vStartup
: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(...);