Poznámka
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
V tomto článku se dozvíte, jak pomocí IHttpClientFactory
rozhraní vytvářet HttpClient
typy s různými základy .NET, jako je injektáž závislostí (DI), protokolování a konfigurace. Typ HttpClient byl zaveden v rozhraní .NET Framework 4.5, který byl vydán v roce 2012. Jinými slovy, je to už nějakou dobu.
HttpClient
slouží k vytváření požadavků HTTP a zpracování odpovědí HTTP z webových prostředků identifikovaných pomocí Uri. Protokol HTTP tvoří velkou většinu veškerého internetového provozu.
Díky moderním principům vývoje aplikací, které určují osvědčené postupy, IHttpClientFactory slouží jako tovární abstrakce, jež může vytvářet instance HttpClient
s vlastními konfiguracemi.
IHttpClientFactory byla zavedena v .NET Core 2.1. Běžné úlohy .NET založené na protokolu HTTP můžou snadno využívat odolný a přechodný middleware pro zpracování chyb třetích stran.
Výstraha
Pokud vaše aplikace vyžaduje cookies, doporučuje se vyhnout se použití IHttpClientFactory. Sdružování HttpMessageHandler instancí vede ke sdílení CookieContainer objektů. Nepředvídané CookieContainer sdílení může vést k úniku souborů cookie mezi nesouvisejícími částmi aplikace. Navíc, když HandlerLifetime vyprší, zprostředkovatel se recykluje, což znamená, že všechny cookies uložené v jeho CookieContainer se ztratí. Alternativní způsoby správy klientů najdete v tématu Pokyny pro používání klientů HTTP.
Důležité
Správa životnosti instancí vytvořených HttpClient
IHttpClientFactory
se zcela liší od instancí vytvořených ručně. Strategie jsou buď použití krátkodobých klientů vytvořených IHttpClientFactory
, nebo dlouhodobých klientů s PooledConnectionLifetime
nastavením. Další informace najdete v části Správa životnosti HttpClient a Pokyny pro používání HTTP klientů.
Typ IHttpClientFactory
Veškerý ukázkový zdrojový kód uvedený v tomto článku vyžaduje instalaci Microsoft.Extensions.Http
balíčku NuGet. Příklady kódu navíc ukazují použití požadavků HTTP GET
k načtení uživatelských Todo
objektů z bezplatného zástupného rozhraní API {JSON}.
Když voláte libovolnou z metod rozšíření AddHttpClient, přidáváte IHttpClientFactory
a související služby do IServiceCollection. Typ IHttpClientFactory
nabízí následující výhody:
- Vystavuje třídu
HttpClient
jako typ připravený pro DI-ready. - Poskytuje centrální umístění pro pojmenování a konfiguraci logických instancí
HttpClient
. - Kodifikuje koncept odchozího middlewaru prostřednictvím delegování obslužných rutin v
HttpClient
. - Poskytuje rozšiřující metody pro Polly založený middleware, které využívají delegování obslužných rutin v
HttpClient
. - Spravuje ukládání do mezipaměti a životnost základních HttpClientHandler instancí. Automatická správa zabraňuje běžným problémům DNS (Domain Name System), ke kterým dochází při ruční správě
HttpClient
životnosti. - Přidá konfigurovatelné záznamování (prostřednictvím ILogger) pro všechny požadavky odeslané prostřednictvím klientů vytvořených v továrně.
Spotřební vzorce
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í
Chcete-li zaregistrovat IHttpClientFactory
, zavolejte AddHttpClient
:
using Shared;
using BasicHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHttpClient();
builder.Services.AddTransient<TodoService>();
using IHost host = builder.Build();
Využívání služeb může vyžadovat parametr konstruktoru IHttpClientFactory
s DI. Následující kód používá IHttpClientFactory
k vytvoření HttpClient
instance:
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Shared;
namespace BasicHttp.Example;
public sealed class TodoService(
IHttpClientFactory httpClientFactory,
ILogger<TodoService> logger)
{
public async Task<Todo[]> GetUserTodosAsync(int userId)
{
// Create the client
HttpClient client = httpClientFactory.CreateClient();
try
{
// Make HTTP GET request
// Parse JSON response deserialize into Todo types
Todo[]? todos = await client.GetFromJsonAsync<Todo[]>(
$"https://jsonplaceholder.typicode.com/todos?userId={userId}",
new JsonSerializerOptions(JsonSerializerDefaults.Web));
return todos ?? [];
}
catch (Exception ex)
{
logger.LogError("Error getting something fun to say: {Error}", ex);
}
return [];
}
}
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 se instance HttpClient
vytvářejí v existující aplikaci, nahraďte výskyty voláním CreateClient.
Pojmenovaní klienti
Pojmenovaní klienti jsou dobrou volbou, když:
- Aplikace vyžaduje mnoho různých použití
HttpClient
. - Mnoho
HttpClient
instancí má různé konfigurace.
Konfiguraci pro pojmenovaný HttpClient
lze zadat během registrace na IServiceCollection
.
using Shared;
using NamedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
string? httpClientName = builder.Configuration["TodoHttpClientName"];
ArgumentException.ThrowIfNullOrEmpty(httpClientName);
builder.Services.AddHttpClient(
httpClientName,
client =>
{
// Set the base address of the named client.
client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
// Add a user-agent default request header.
client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
});
V předchozím kódu je klient nakonfigurovaný takto:
- Název, který je vytáhnut z konfigurace pod
"TodoHttpClientName"
. - Základní adresa
https://jsonplaceholder.typicode.com/
. - Záhlaví
"User-Agent"
.
Konfiguraci můžete použít k určení názvů klientů HTTP, což je užitečné, abyste se vyhnuli nesprávnému přejmenování klientů při přidávání a vytváření. V tomto příkladu se k konfiguraci názvu klienta HTTP používá soubor appsettings.json :
{
"TodoHttpClientName": "JsonPlaceholderApi"
}
Tuto konfiguraci můžete snadno rozšířit a uložit další podrobnosti o tom, jak má klient HTTP fungovat. Další informace naleznete v tématu Konfigurace v .NET.
Vytvoření klienta
CreateClient je volán pokaždé:
- Vytvoří se nová instance
HttpClient
. - Akce konfigurace je vyvolána.
Pokud chcete vytvořit pojmenovaného klienta, předejte jeho název do CreateClient
:
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Shared;
namespace NamedHttp.Example;
public sealed class TodoService
{
private readonly IHttpClientFactory _httpClientFactory = null!;
private readonly IConfiguration _configuration = null!;
private readonly ILogger<TodoService> _logger = null!;
public TodoService(
IHttpClientFactory httpClientFactory,
IConfiguration configuration,
ILogger<TodoService> logger) =>
(_httpClientFactory, _configuration, _logger) =
(httpClientFactory, configuration, logger);
public async Task<Todo[]> GetUserTodosAsync(int userId)
{
// Create the client
string? httpClientName = _configuration["TodoHttpClientName"];
HttpClient client = _httpClientFactory.CreateClient(httpClientName ?? "");
try
{
// Make HTTP GET request
// Parse JSON response deserialize into Todo type
Todo[]? todos = await client.GetFromJsonAsync<Todo[]>(
$"todos?userId={userId}",
new JsonSerializerOptions(JsonSerializerDefaults.Web));
return todos ?? [];
}
catch (Exception ex)
{
_logger.LogError("Error getting something fun to say: {Error}", ex);
}
return [];
}
}
V předchozím kódu požadavek HTTP 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.
- Poskytněte nápovědu IntelliSense a pomoc kompilátoru při používání klienty.
- 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 backendový koncový bod.
- Zapouzdřit veškerou logiku, která se zabývá koncovými body.
- Pracujte s DI a může být implementováno tam, kde je to vyžadováno v aplikaci.
Typovaný klient přijímá parametr HttpClient
v jeho konstruktoru.
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Shared;
namespace TypedHttp.Example;
public sealed class TodoService(
HttpClient httpClient,
ILogger<TodoService> logger)
{
public async Task<Todo[]> GetUserTodosAsync(int userId)
{
try
{
// Make HTTP GET request
// Parse JSON response deserialize into Todo type
Todo[]? todos = await httpClient.GetFromJsonAsync<Todo[]>(
$"todos?userId={userId}",
new JsonSerializerOptions(JsonSerializerDefaults.Web));
return todos ?? [];
}
catch (Exception ex)
{
logger.LogError("Error getting something fun to say: {Error}", ex);
}
return [];
}
}
V předchozím kódu:
- Konfigurace se nastaví při přidání zadaného klienta do kolekce služeb.
-
HttpClient
je přiřazena jako proměnná ve třídním rozsahu (pole) a používá se s vystavenými API.
Metody specifické pro rozhraní API je možné vytvořit, které zpřístupňují HttpClient
funkce. Například metoda GetUserTodosAsync
zapouzdřuje kód pro načtení uživatelsky specifických objektů Todo
.
Následující kód volá AddHttpClient k registraci typové třídy klienta:
using Shared;
using TypedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHttpClient<TodoService>(
client =>
{
// Set the base address of the typed client.
client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
// Add a user-agent default request header.
client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
});
Zadaný klient je zaregistrovaný jako přechodný s DI. V předchozím kódu AddHttpClient
se zaregistruje TodoService
jako přechodná služba. Tato registrace používá tovární metodu k:
- Vytvořte instanci
HttpClient
. - Vytvořte instanci
TodoService
, přičemž předáte instanciHttpClient
do jejího konstruktoru.
Důležité
Používání typových klientů v singletonových službách může být nebezpečné. Další informace najdete v části Vyhnout se typed klientům v singleton services.
Poznámka:
Při registraci zadaného klienta metodou AddHttpClient<TClient>
typu TClient
musí mít typ konstruktor, který přijímá jako parametr HttpClient
. Kromě toho by se typ TClient
neměl registrovat u kontejneru DI samostatně, protože to povede k pozdější registraci přepisující předchozí.
Vygenerované klienty
IHttpClientFactory
lze použít v kombinaci s knihovnami třetích stran, jako je například Refit. Refit je knihovna REST pro .NET. Umožňuje deklarativní definice rozhraní REST API, metody mapování rozhraní na koncové body. Implementace rozhraní je generována dynamicky pomocí RestService
, který využívá HttpClient
k provedení externích volání HTTP.
Zvažte následující record
typ:
namespace Shared;
public record class Todo(
int UserId,
int Id,
string Title,
bool Completed);
Následující příklad spoléhá na Refit.HttpClientFactory
balíček NuGet a je to jednoduché rozhraní:
using Refit;
using Shared;
namespace GeneratedHttp.Example;
public interface ITodoService
{
[Get("/todos?userId={userId}")]
Task<Todo[]> GetUserTodosAsync(int userId);
}
Předchozí rozhraní jazyka C#:
- Definuje metodu s názvem
GetUserTodosAsync
, která vracíTask<Todo[]>
instanci. - Deklaruje
Refit.GetAttribute
atribut s cestou a řetězcem dotazu pro externí rozhraní API.
Typový klient je možné přidat pomocí nástroje Refit k vygenerování implementace:
using GeneratedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Refit;
using Shared;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddRefitClient<ITodoService>()
.ConfigureHttpClient(client =>
{
// Set the base address of the named client.
client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
// Add a user-agent default request header.
client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
});
Definované rozhraní lze v případě potřeby využívat s implementací, kterou poskytuje DI a Refit.
Vytváření požadavků POST, PUT a DELETE
V předchozích příkladech používají všechny požadavky HTTP metodu GET
.
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. Další informace o vytváření požadavků HTTP naleznete v tématu Odeslání požadavku pomocí HttpClient.
Následující příklad ukazuje, jak vytvořit požadavek HTTP POST
:
public async Task CreateItemAsync(Item item)
{
using StringContent json = new(
JsonSerializer.Serialize(item, new JsonSerializerOptions(JsonSerializerDefaults.Web)),
Encoding.UTF8,
MediaTypeNames.Application.Json);
using HttpResponseMessage httpResponse =
await httpClient.PostAsync("/api/items", json);
httpResponse.EnsureSuccessStatusCode();
}
V předchozím kódu metoda CreateItemAsync
:
- Serializuje
Item
parametr do FORMÁTU JSON pomocíSystem.Text.Json
. To používá instanci JsonSerializerOptions ke konfiguraci procesu serializace. - Vytvoří instanci StringContent pro zabalení serializovaného JSONu k 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.
- Zavolá EnsureSuccessStatusCode, aby vyvolal výjimku, pokud stavový kód odpovědi neukazuje na úspěch.
HttpClient
podporuje také jiné typy obsahu. Příklad: MultipartContent a StreamContent. Úplný seznam podporovaného obsahu naleznete viz HttpContent.
Následující příklad ukazuje požadavek HTTP PUT
:
public async Task UpdateItemAsync(Item item)
{
using StringContent json = new(
JsonSerializer.Serialize(item, new JsonSerializerOptions(JsonSerializerDefaults.Web)),
Encoding.UTF8,
MediaTypeNames.Application.Json);
using HttpResponseMessage httpResponse =
await httpClient.PutAsync($"/api/items/{item.Id}", json);
httpResponse.EnsureSuccessStatusCode();
}
Předchozí kód je velmi podobný příkladu POST
. Metoda UpdateItemAsync
volá PutAsync místo PostAsync
.
Následující příklad ukazuje požadavek HTTP DELETE
:
public async Task DeleteItemAsync(Guid id)
{
using HttpResponseMessage httpResponse =
await httpClient.DeleteAsync($"/api/items/{id}");
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.
HttpClient
správa životnosti
Nová HttpClient
instance se vrátí pokaždé, když CreateClient
je volána na IHttpClientFactory
. Pro každý název klienta se vytvoří jedna HttpClientHandler instance. Továrna spravuje životní cykly HttpClientHandler
instancí.
IHttpClientFactory
HttpClientHandler
ukládá instance vytvořené továrnou do mezipaměti, aby se snížila spotřeba prostředků. Instance HttpClientHandler
může být znovu použita z mezipaměti při vytváření nové HttpClient
instance, pokud jeho životnost nevypršela.
Ukládání obslužných rutin do mezipaměti je žádoucí, protože každá obslužná rutina obvykle spravuje svůj vlastní základní fond připojení HTTP. Vytvoření více obslužných rutin, než je potřeba, může vést k vyčerpání soketů a 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í doba platnosti obslužné rutiny je dvě minuty. Pokud chcete přepsat výchozí hodnotu, zavolejte SetHandlerLifetime pro každého klienta na IServiceCollection
:
services.AddHttpClient("Named.Client")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
Důležité
HttpClient
Instance vytvořené pomocí IHttpClientFactory
jsou určeny k krátkodobému použití.
Recyklace a opětovné vytvoření
HttpMessageHandler
po jejich vypršení životnosti je nezbytné proIHttpClientFactory
, aby obslužné rutiny reagovaly na změny DNS.HttpClient
je svázaný s konkrétní instancí obslužné rutiny při jeho vytvoření, takže novéHttpClient
instance by měly být požadovány včas, aby se zajistilo, že klient získá aktualizovanou obslužnou rutinu.Vyřazení takových
HttpClient
instancí vytvořených továrnou nebude vést k vyčerpání soketů, protože jeho odstranění nespustí odstraněníHttpMessageHandler
.IHttpClientFactory
sleduje a odstraňuje prostředky používané k vytvářeníHttpClient
instancí, konkrétněHttpMessageHandler
instancí, jakmile jejich životnost vyprší a už je nepoužíváHttpClient
.
Udržování jediné instance PooledConnectionLifetime
, nebo krátkodobé klienty vytvořené pomocí IHttpClientFactory
. Informace o tom, jakou strategii použít ve vaší aplikaci, najdete v tématu Pokyny pro používání klientů HTTP.
Nakonfigurujte HttpMessageHandler
Může být nutné řídit konfiguraci vnitřního HttpMessageHandler prostředí používaného klientem.
Je vráceno IHttpClientBuilder při přidávání pojmenovaných či zadaných klientů. Metoda rozšíření ConfigurePrimaryHttpMessageHandler může být použita k definování delegáta na IServiceCollection
. Delegát se používá k vytvoření a konfiguraci primárního HttpMessageHandler
, který tento klient používá.
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler
{
AllowAutoRedirect = false,
UseDefaultCredentials = true
};
});
Konfigurace HttClientHandler
umožňuje zadat proxy pro HttpClient
instanci mezi různými dalšími vlastnostmi obslužné rutiny. Další informace najdete v tématu Proxy na klienta.
Další konfigurace
Existuje několik dalších možností konfigurace pro řízení IHttpClientHandler
:
metoda | Popis |
---|---|
AddHttpMessageHandler | Přidá další obslužnou rutinu zprávy pro pojmenovaný objekt HttpClient . |
AddTypedClient | Konfiguruje vazbu mezi TClient a pojmenovanou HttpClient přidruženou k objektu IHttpClientBuilder . |
ConfigureHttpClient | Přidá delegáta, který se použije ke konfiguraci entity s názvem HttpClient . |
ConfigurePrimaryHttpMessageHandler | Nakonfiguruje primární HttpMessageHandler z kontejneru pro injektování závislostí pro pojmenovanou HttpClient . |
RedactLoggedHeaders | Nastaví kolekci názvů hlaviček HTTP, pro které mají být hodnoty před protokolováním skryty. |
SetHandlerLifetime | Nastaví dobu, po kterou HttpMessageHandler může být instance znovu použita. Každý pojmenovaný klient může mít svou vlastní nakonfigurovanou hodnotu životnosti obsluhy. |
UseSocketsHttpHandler | Nakonfiguruje novou nebo dříve přidanou SocketsHttpHandler instanci z kontejneru injektáže závislostí tak, aby byla použita jako primární obslužná rutina pro pojmenovanou HttpClient . (pouze pro .NET 5+) |
Použití IHttpClientFactory společně se SocketsHttpHandler
Implementace SocketsHttpHandler
HttpMessageHandler
byla přidána v .NET Core 2.1, což umožňuje konfigurovat PooledConnectionLifetime
. Toto nastavení slouží k zajištění toho, aby obslužná rutina reagovala na změny DNS, takže použití SocketsHttpHandler
se považuje za alternativu k použití IHttpClientFactory
. Další informace najdete v tématu Pokyny pro používání klientů HTTP.
SocketsHttpHandler
a IHttpClientFactory
mohou být použity společně k zlepšení konfigurovatelnosti. Pomocí obou těchto rozhraní API můžete využít možnosti konfigurace na nízké úrovni (například pro LocalCertificateSelectionCallback
výběr dynamického certifikátu) a vysokou úroveň (například využití integrace DI a několika konfigurací klientů).
Použití obou rozhraní API:
- Zadejte
SocketsHttpHandler
jakoPrimaryHandler
via ConfigurePrimaryHttpMessageHandlernebo UseSocketsHttpHandler (pouze .NET 5+). - Nastavte SocketsHttpHandler.PooledConnectionLifetime na základě intervalu, který očekáváte, že se DNS aktualizuje, například na hodnotu, která byla dříve v
HandlerLifetime
. - (Volitelné) Vzhledem k tomu, že
SocketsHttpHandler
bude zpracovávat sdružování a recyklaci připojení, není na úrovniIHttpClientFactory
recyklace obslužné rutiny již nutná. Můžete ho zakázat nastavenímHandlerLifetime
naTimeout.InfiniteTimeSpan
.
services.AddHttpClient(name)
.UseSocketsHttpHandler((handler, _) =>
handler.PooledConnectionLifetime = TimeSpan.FromMinutes(2)) // Recreate connection every 2 minutes
.SetHandlerLifetime(Timeout.InfiniteTimeSpan); // Disable rotation, as it is handled by PooledConnectionLifetime
V předchozím příkladu byly pro ilustraci vybrány libovolně 2 minuty, které odpovídají výchozí HandlerLifetime
hodnotě. Měli byste zvolit hodnotu na základě očekávané frekvence DNS nebo jiných změn sítě. Další informace najdete v části chování DNS v HttpClient
pokynech a v části Poznámky v PooledConnectionLifetime dokumentaci k rozhraní API.
Vyhněte se typovým klientům v singletonových službách
Při použití přístupu s pojmenovaným klientem se vloží do služeb a instance IHttpClientFactory
se vytvoří voláním HttpClient
pokaždé, když je CreateClient potřeba.
Při přístupu typu klienta jsou však typoví klienti přechodnými objekty obvykle vloženými do služeb. To může způsobit problém, protože typový klient lze vložit do singletonové služby.
Důležité
Očekává se, že typoví klienti budou krátkodobí ve stejném smyslu jako HttpClient
instance vytvořené pomocí IHttpClientFactory
(další informace viz HttpClient
správa životnosti). Jakmile se vytvoří zatypovaná instance klienta, IHttpClientFactory
nemá nad ní žádnou kontrolu. Pokud je instance typového klienta zachycena v jedináčku, může jí to zabránit reagovat na změny DNS, což narušuje jeden z účelů IHttpClientFactory
.
Pokud potřebujete používat HttpClient
instance ve službě singleton, zvažte následující možnosti:
- Místo toho použijte přístup s pojmenovaným klientem, vložte
IHttpClientFactory
do singleton služby a v případě potřeby znovu vytvořte instanceHttpClient
. - Pokud potřebujete typizovaný přístup klienta, použijte
SocketsHttpHandler
s nakonfigurovanýmPooledConnectionLifetime
jako primárního obslužného prvku. Další informace o použitíSocketsHttpHandler
sIHttpClientFactory
naleznete v části Použití IHttpClientFactory společně se SocketsHttpHandler.
Rozsahy zpracovatelů zpráv v IHttpClientFactory
IHttpClientFactory
vytvoří pro každou HttpMessageHandler
instanci samostatný obor DI. Tyto obory DI jsou oddělené od oborů DI aplikací (například ASP.NET rozsah příchozích požadavků nebo uživatelem vytvořený ruční obor DI), takže nebudou sdílet instance služby s vymezeným oborem. Rozsahy obslužné rutiny zpráv jsou svázány s životností obslužné rutiny a mohou přežít rozsahy aplikace, což může vést například k opakovanému použití stejné instance se stejnými HttpMessageHandler
vloženými vymezenými závislostmi mezi několika příchozími požadavky.
Uživatelé důrazně nedoporučuje ukládat informace související s oborem (například data z HttpContext
) do mezipaměti v HttpMessageHandler
instancích a používat vymezené závislosti s opatrností, aby nedošlo k úniku citlivých informací.
Pokud potřebujete přístup k oboru DI aplikace z obslužné rutiny zprávy, například pro účely autentizace, doporučuje se zapouzdřit logiku řízení oboru do samostatného přechodného DelegatingHandler
a obalit ji okolo instance HttpMessageHandler
z mezipaměti IHttpClientFactory
. Přístup k volání IHttpMessageHandlerFactory.CreateHandler obslužné rutiny pro všechny registrované pojmenované klienty. V takovém případě byste si sami vytvořili instanci HttpClient
pomocí sestavené obslužné rutiny.
Následující příklad ukazuje, jak vytvořit HttpClient
, který je si vědom rozsahu DelegatingHandler
.
if (scopeAwareHandlerType != null)
{
if (!typeof(DelegatingHandler).IsAssignableFrom(scopeAwareHandlerType))
{
throw new ArgumentException($"""
Scope aware HttpHandler {scopeAwareHandlerType.Name} should
be assignable to DelegatingHandler
""");
}
// Create top-most delegating handler with scoped dependencies
scopeAwareHandler = (DelegatingHandler)_scopeServiceProvider.GetRequiredService(scopeAwareHandlerType); // should be transient
if (scopeAwareHandler.InnerHandler != null)
{
throw new ArgumentException($"""
Inner handler of a delegating handler {scopeAwareHandlerType.Name} should be null.
Scope aware HttpHandler should be registered as Transient.
""");
}
}
// Get or create HttpMessageHandler from HttpClientFactory
HttpMessageHandler handler = _httpMessageHandlerFactory.CreateHandler(name);
if (scopeAwareHandler != null)
{
scopeAwareHandler.InnerHandler = handler;
handler = scopeAwareHandler;
}
HttpClient client = new(handler);
Další řešení může být použito metodou rozšíření pro registraci DelegatingHandler
s ohledem na obor a pro přepsání výchozí registrace IHttpClientFactory
pomocí přechodné služby, která má přístup k aktuálnímu oboru aplikace.
public static IHttpClientBuilder AddScopeAwareHttpHandler<THandler>(
this IHttpClientBuilder builder) where THandler : DelegatingHandler
{
builder.Services.TryAddTransient<THandler>();
if (!builder.Services.Any(sd => sd.ImplementationType == typeof(ScopeAwareHttpClientFactory)))
{
// Override default IHttpClientFactory registration
builder.Services.AddTransient<IHttpClientFactory, ScopeAwareHttpClientFactory>();
}
builder.Services.Configure<ScopeAwareHttpClientFactoryOptions>(
builder.Name, options => options.HttpHandlerType = typeof(THandler));
return builder;
}
Další informace najdete v úplném příkladu.
Vyhněte se spoléhání na výchozí nastavení primární obslužné rutiny.
V této části termín "výchozí" primární obslužná rutina odkazuje na primární obslužnou rutinu, kterou výchozí implementace IHttpClientFactory
(nebo přesněji, výchozí HttpMessageHandlerBuilder
implementace) přiřadí, pokud nenakonfigurováno žádným způsobem vůbec.
Poznámka:
Hlavní obslužná rutina v továrním nastavení je implementační detail a může být změněna.
❌ VYHNĚTE se spoléhání na konkrétní implementaci, která se používá jako "tovární nastavení" (například HttpClientHandler
).
Existují případy, kdy potřebujete znát konkrétní typ primárního zpracovatele, zejména pokud pracujete na knihovně tříd. Při zachování konfigurace koncového uživatele můžete chtít aktualizovat například vlastnosti specifické pro HttpClientHandler
, jako jsou ClientCertificates
, UseCookies
a UseProxy
. Může být lákavé přetypovat primární obslužnou rutinu na HttpClientHandler
, která náhodou fungovala, zatímco byla použita jako "továrně nastavená" primární obslužná rutina. Stejně jako každý kód závislý na detailech implementace je takové alternativní řešení křehké a náchylné k selhání.
Místo spolehnutí se na "výchozí tovární nastavení" pro primární obslužnou rutinu můžete použít ConfigureHttpClientDefaults
k nastavení výchozí instance primární obslužné rutiny na úrovni aplikace.
// Contract with the end-user: Only HttpClientHandler is supported.
// --- "Pre-configure" stage ---
// The default is fixed as HttpClientHandler to avoid depending on the "factory-default"
// Primary Handler.
services.ConfigureHttpClientDefaults(b =>
b.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() { UseCookies = false }));
// --- "End-user" stage ---
// IHttpClientBuilder builder = services.AddHttpClient("test", /* ... */);
// ...
// --- "Post-configure" stage ---
// The code can rely on the contract, and cast to HttpClientHandler only.
builder.ConfigurePrimaryHttpMessageHandler((handler, provider) =>
{
if (handler is not HttpClientHandler h)
{
throw new InvalidOperationException("Only HttpClientHandler is supported");
}
h.ClientCertificates.Add(GetClientCert(provider, builder.Name));
//X509Certificate2 GetClientCert(IServiceProvider p, string name) { ... }
});
Alternativně můžete zvážit kontrolu typu primární obslužné rutiny a nakonfigurovat specifika, jako jsou klientské certifikáty, pouze ve známých podpůrných typech (s největší pravděpodobností, HttpClientHandler
a SocketsHttpHandler
):
// --- "End-user" stage ---
// IHttpClientBuilder builder = services.AddHttpClient("test", /* ... */);
// ...
// --- "Post-configure" stage ---
// No contract is in place. Trying to configure main handler types supporting client
// certs, logging and skipping otherwise.
builder.ConfigurePrimaryHttpMessageHandler((handler, provider) =>
{
if (handler is HttpClientHandler h)
{
h.ClientCertificates.Add(GetClientCert(provider, builder.Name));
}
else if (handler is SocketsHttpHandler s)
{
s.SslOptions ??= new System.Net.Security.SslClientAuthenticationOptions();
s.SslOptions.ClientCertificates ??= new X509CertificateCollection();
s.SslOptions.ClientCertificates!.Add(GetClientCert(provider, builder.Name));
}
else
{
// Log warning
}
//X509Certificate2 GetClientCert(IServiceProvider p, string name) { ... }
});