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.
Běžné problémy s používáním
V tomto článku se dozvíte o některých nejběžnějších problémech, na které můžete narazit při vytváření IHttpClientFactoryHttpClient instancí.
IHttpClientFactory je pohodlný způsob nastavení více HttpClient konfigurací v kontejneru DI, konfigurace protokolování, nastavení strategií odolnosti a další.
IHttpClientFactory také zapouzdřuje správu životnosti instancí HttpClient a HttpMessageHandler, aby se zabránilo problémům, jako je vyčerpání socketů a ztráta změn DNS. Pro přehled o tom, jak používat IHttpClientFactory ve vaší aplikaci .NET, se podívejte na IHttpClientFactory s .NET.
Vzhledem ke složité povaze IHttpClientFactory integrace s DI můžete narazit na problémy, které mohou být obtížné zachytit a odstraňovat. Scénáře uvedené v tomto článku obsahují také doporučení, která můžete aktivně použít, abyste se vyhnuli potenciálním problémům.
HttpClient nerespektuje Scoped životnost
Můžete narazit na problém, pokud potřebujete získat přístup k jakékoli vymezené službě, například HttpContext, nebo k některé vymezené mezipaměti v rámci HttpMessageHandler. Data uložená tam mohou buď "zmizet", nebo naopak, "zůstat", když by neměla. Příčinou je nesoulad rozsahu Dependency Injection (DI) mezi kontextem aplikace a instancí obslužné rutiny, což je známé omezení v IHttpClientFactory.
IHttpClientFactory vytvoří pro každou HttpMessageHandler instanci samostatný obor DI. Tyto obory obslužné rutiny se liší od oborů kontextu aplikace (například ASP.NET rozsah příchozích požadavků Core nebo uživatelem vytvořený ruční obor DI), takže nebudou sdílet instance služby s vymezeným oborem.
V důsledku tohoto omezení:
- Veškerá data uložená v mezipaměti "externě" v rámci služby s vymezeným oborem nebudou v rámci této
HttpMessageHandlerslužby k dispozici . - Veškerá data uložená v mezipaměti "internálně" uvnitř
HttpMessageHandlernebo jeho vymezených závislostí lze pozorovat z více oborů DI aplikace (například z různých příchozích požadavků), protože mohou sdílet stejný handler.
Zvažte následující doporučení, která vám pomůžou zmírnit toto známé omezení:
❌ NEukládejte žádné informace souvisící s rozsahem (například data z HttpContext) v instancích HttpMessageHandler nebo jejich závislostech, aby nedošlo k úniku citlivých informací.
❌ NEPOUŽÍVEJTE soubory cookie, protože CookieContainer budou sdíleny společně s handlerem.
✔️ ZVAŽTE, zda informace neuložit nebo je předávat pouze v instanci HttpRequestMessage.
K předání libovolných informací vedle objektu HttpRequestMessage, můžete použít HttpRequestMessage.Options vlastnost.
✔️ ZVAŽTE zapouzdření veškeré logiky související s oborem (například ověřování) v samostatném DelegatingHandler objektu, který není vytvořen objektem IHttpClientFactory, a použijte ji k zabalení IHttpClientFactoryobslužné rutiny -created.
Chcete-li vytvořit pouze HttpMessageHandler bez HttpClient, zavolejte IHttpMessageHandlerFactory.CreateHandler pro libovolného registrovaného pojmenovaného klienta. V takovém případě budete muset instanci HttpClient vytvořit sami pomocí kombinovaného obslužného prvku. Plně spustitelný příklad tohoto alternativního řešení najdete na GitHubu.
Další informace najdete v části Obory obslužné rutiny zpráv v IHttpClientFactory v dokumentaci IHttpClientFactory.
HttpClient nerespektuje změny DNS
I když IHttpClientFactory se používá, je stále možné narazit na zastaralý problém DNS. K tomu obvykle může dojít v případě, že instance HttpClient je zachycena ve službě Singleton, nebo je obecně uložena někam po dobu delší, než je zadané HandlerLifetime.
HttpClient se také zachytí, pokud je příslušný typový klient zachycen singletonem.
❌ Neukládejte do mezipaměti HttpClient instance vytvořená IHttpClientFactory na delší dobu.
❌ Nevkládejte instance typed klienta do Singleton služeb.
✔️ ZVAŽTE požádat včas o klienta IHttpClientFactory nebo pokaždé, když ho potřebujete. Klienti vytvoření továrním způsobem jsou bezpečně odstraněni.
HttpClient Instance vytvořené pomocí IHttpClientFactory jsou určeny k krátkodobému použití.
Recyklace a opětovné vytvoření
HttpMessageHandlerpo vypršení jejich životnosti je nezbytné,IHttpClientFactoryaby obslužné rutiny reagovaly na změny DNS.HttpClientje svázaný s konkrétní instancí obslužné rutiny při jeho vytvoření, takže novéHttpClientinstance 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
HttpClientinstancí vytvořených továrnou nepovede k vyčerpání soketů, protože odstranění těchto instancí nepůsobí na likvidaciHttpMessageHandler.IHttpClientFactorysleduje a odstraňuje prostředky používané k vytvářeníHttpClientinstancí, konkrétněHttpMessageHandlerinstancí, jakmile jejich životnost vyprší a už je nepoužíváHttpClient.
Typoví klienti mají být krátkodobí, protože HttpClient instance je vložena do konstruktoru, takže bude sdílet životnost typového klienta.
Další informace naleznete v sekcích HttpClient řízení životnosti a Jak se vyhnout typu klientů v singleton službách v IHttpClientFactory pokynech.
HttpClient používá příliš mnoho soketů.
I když se používá IHttpClientFactory, stále je možné narazit na problém s vyčerpáním soketů při konkrétním scénáři použití. Ve výchozím nastavení HttpClient neomezuje počet souběžných požadavků. Pokud se současně spustí velký počet požadavků HTTP/1.1, každý z nich nakonec aktivuje nový pokus o připojení HTTP, protože ve fondu není žádné bezplatné připojení a není nastavené žádné omezení.
❌ Nespouštějte velký počet požadavků HTTP/1.1 současně bez určení limitů.
✔️ ZVAŽTE nastavení HttpClientHandler.MaxConnectionsPerServer (nebo SocketsHttpHandler.MaxConnectionsPerServer, pokud ho použijete jako primární obslužnou rutinu) na rozumnou hodnotu. Všimněte si, že tato omezení platí pouze pro konkrétní instanci obslužné rutiny.
✔️ ZVAŽTE POUŽITÍ PROTOKOLU HTTP/2, které umožňuje multiplexování požadavků přes jedno připojení TCP.
Zadaný klient má nesprávnou HttpClient injektáž
Mohou existovat různé situace, kdy je možné zažít neočekávané vložení HttpClient do zadaného klienta. Ve většině případů bude původní příčina chybná konfigurace, protože návrh DI přepíše předchozí registraci služby.
TClient. To je první z TClient,TImplementation dvojice, pokud se AddHttpClient<TClient,TImplementation> použijí přetížení.
Proto registrace typového klienta dělá dvě samostatné věci:
- Zaregistruje pojmenovaného klienta (v jednoduchém výchozím případě je
typeof(TClient).Namenázev ). - Zaregistruje
Transientslužbu pomocí poskytnutéhoTClientneboTClient,TImplementation.
Následující dva příkazy jsou technicky stejné:
services.AddHttpClient<ExampleClient>(c => c.BaseAddress = new Uri("http://example.com"));
// -OR-
services.AddHttpClient(nameof(ExampleClient), c => c.BaseAddress = new Uri("http://example.com")) // register named client
.AddTypedClient<ExampleClient>(); // link the named client to a typed client
V jednoduchém případě se bude podobat také následujícímu:
services.AddHttpClient(nameof(ExampleClient), c => c.BaseAddress = new Uri("http://example.com")); // register named client
// register plain Transient service and link it to the named client
services.AddTransient<ExampleClient>(s =>
new ExampleClient(
s.GetRequiredService<IHttpClientFactory>().CreateClient(nameof(ExampleClient))));
Podívejte se na následující příklady, jak může dojít k přerušení propojení mezi zadanými a pojmenovanými klienty.
Typovaný klient se zaregistruje podruhé.
❌ NEZAREGISTRUJTE zadaného klienta samostatně – už je automaticky registrováno voláním AddHttpClient<T> .
Pokud je typový klient chybně zaregistrován podruhé jako prostá přechodná služba, přepíše to registraci přidanou IHttpClientFactory, a přeruší tak propojení s pojmenovaným klientem. Bude se zdát, jako by došlo ke ztrátě konfigurace HttpClient, protože nekonfigurovaný HttpClient bude použit ve zadaném klientovi.
Může být matoucí, že místo vyvolání výjimky se použije chyba HttpClient . K tomu dochází, protože "výchozí" nekonfigurovaný HttpClient — klient s názvem Options.DefaultName — je zaregistrovaný jako jednoduchá přechodná služba string.Empty, aby se umožnilo nejzákladnější scénář použití IHttpClientFactory. To je důvod, proč když se přeruší propojení a typový klient se stane jen běžnou službou, tato "výchozí" hodnoty se přirozeně vloží do příslušného parametru konstruktoru.
Různí typoví klienti se registrují na společném rozhraní.
V případě, že jsou na společném rozhraní zaregistrovaní dva různí klienti typu, budou oba znovu používat stejného pojmenovaného klienta. Může to vypadat jako první typovaný klient, který byl nesprávně injektován do druhého pojmenovaného klienta.
❌ NEZAREGISTRUJTE více typech klientů v jednom rozhraní bez explicitního zadání názvu.
✔️ ZVAŽTE registraci a konfiguraci pojmenovaného klienta samostatně a poté ho propojit s jedním nebo více zadanými klienty, buď zadáním názvu ve volání AddHttpClient<T>, nebo voláním AddTypedClient během instalace pojmenovaného klienta.
Při návrhu se registrace a konfigurace pojmenovaného klienta se stejným názvem několikrát připojí akce konfigurace k seznamu existujících klientů . Toto chování IHttpClientFactory nemusí být zřejmé, ale je to stejný přístup, jaký využívá Options pattern a konfigurační API, jako Configure.
To je většinou užitečné pro pokročilé konfigurace obslužné rutiny, například přidání vlastní obslužné rutiny do pojmenovaného klienta definované externě nebo napodobování primární obslužné rutiny pro testy, ale funguje také pro HttpClient konfiguraci instance. Například tři následující příklady budou mít za následek HttpClient konfiguraci stejným způsobem (obě BaseAddress a DefaultRequestHeaders jsou nastaveny):
// one configuration callback
services.AddHttpClient("example", c =>
{
c.BaseAddress = new Uri("http://example.com");
c.DefaultRequestHeaders.UserAgent.ParseAdd("HttpClient/8.0");
});
// -OR-
// two configuration callbacks
services.AddHttpClient("example", c => c.BaseAddress = new Uri("http://example.com"))
.ConfigureHttpClient(c => c.DefaultRequestHeaders.UserAgent.ParseAdd("HttpClient/8.0"));
// -OR-
// two configuration callbacks in separate AddHttpClient calls
services.AddHttpClient("example", c => c.BaseAddress = new Uri("http://example.com"));
services.AddHttpClient("example")
.ConfigureHttpClient(c => c.DefaultRequestHeaders.UserAgent.ParseAdd("HttpClient/8.0"));
To umožňuje propojit typového klienta s již definovaným pojmenovaným klientem a také propojit několik typových klientů s jedním pojmenovaným klientem. Je to zřetelnější, když jsou použita přetížení s parametrem name.
services.AddHttpClient("LogClient", c => c.BaseAddress = new Uri(LogServerAddress));
services.AddHttpClient<FooLogger>("LogClient");
services.AddHttpClient<BarLogger>("LogClient");
Stejnou věc lze dosáhnout také voláním AddTypedClient během pojmenované konfigurace klienta :
services.AddHttpClient("LogClient", c => c.BaseAddress = new Uri(LogServerAddress))
.AddTypedClient<FooLogger>()
.AddTypedClient<BarLogger>();
Pokud ale nechcete znovu používat stejného pojmenovaného klienta, ale přesto chcete zaregistrovat klienty ve stejném rozhraní, můžete to udělat tak, že explicitně zadáte pro ně různé názvy:
services.AddHttpClient<ITypedClient, ExampleClient>(nameof(ExampleClient),
c => c.BaseAddress = new Uri("http://example.com"));
services.AddHttpClient<ITypedClient, GithubClient>(nameof(GithubClient),
c => c.BaseAddress = new Uri("https://github.com"));