Yaygın IHttpClientFactory
kullanım sorunları
Bu makalede, kullanarak örnek oluştururken IHttpClientFactory
HttpClient
karşılaşabileceğiniz en yaygın sorunlardan bazılarını öğreneceksiniz.
IHttpClientFactory
DI kapsayıcısında birden çok HttpClient
yapılandırma ayarlamanın, günlüğe kaydetmeyi yapılandırmanın, dayanıklılık stratejilerini ayarlamanın ve daha fazlasının kullanışlı bir yoludur. IHttpClientFactory
ayrıca yuva tükenmesi ve DNS değişikliklerini kaybetme gibi sorunları önlemek için ve HttpMessageHandler
örneklerinin HttpClient
yaşam süresi yönetimini kapsüller. .NET uygulamanızda nasıl kullanılacağına IHttpClientFactory
genel bakış için bkz . .NET ile IHttpClientFactory.
DI ile tümleştirmenin IHttpClientFactory
karmaşık yapısı nedeniyle, yakalaması ve gidermesi zor olabilecek bazı sorunlarla karşılaşabilirsiniz. Bu makalede listelenen senaryolar, olası sorunları önlemek için proaktif olarak uygulayabileceğiniz öneriler de içerir.
HttpClient
yaşam süresine saygı Scoped
göstermez
kapsamındaki herhangi bir hizmete(örneğin, HttpContext
) veya bazı kapsamlı önbelleklere içinden HttpMessageHandler
erişmeniz gerekiyorsa bir sorunla karşılaşırsınız. Oraya kaydedilen veriler "kaybolabilir" veya başka bir deyişle, "kalıcı" duruma gelebilir. Bunun nedeni uygulama bağlamı ile işleyici örneği arasındaki Bağımlılık Ekleme (DI) kapsamı uyuşmazlığıdır ve içinde IHttpClientFactory
bilinen bir sınırlamadır.
IHttpClientFactory
her HttpMessageHandler
örnek için ayrı bir DI kapsamı oluşturur. Bu işleyici kapsamları uygulama bağlamı kapsamlarından (örneğin, ASP.NET Çekirdek gelen istek kapsamı veya kullanıcı tarafından oluşturulan bir el ile DI kapsamı) farklıdır, bu nedenle kapsamlı hizmet örneklerini paylaşmaz.
Bu sınırlamanın bir sonucu olarak:
- Kapsamı belirlenmiş bir hizmette "harici olarak" önbelleğe alınan hiçbir veri içinde
HttpMessageHandler
kullanılamaz. - veya kapsamındaki bağımlılıkları içinde
HttpMessageHandler
"dahili olarak" önbelleğe alınan tüm veriler, aynı işleyiciyi paylaşabilecekleri için birden çok uygulama DI kapsamından (örneğin, farklı gelen isteklerden) gözlemlenebilir.
Bu bilinen sınırlamayı hafifletmeye yardımcı olmak için aşağıdaki önerileri göz önünde bulundurun:
❌ Hassas bilgilerin sızmasını önlemek için kapsamla ilgili bilgileri (örneğin, içindeki veriler HttpContext
) örneklerin veya bağımlılıklarının içinde HttpMessageHandler
önbelleğe ALMAYIN.
❌ İşleyici ile paylaşılacağından CookieContainer
tanımlama bilgilerini KULLANMAYIN.
✔️ Bilgileri depolamamayı veya yalnızca örnekte geçirmeyi HttpRequestMessage
GÖZ ÖNÜNDE BULUNDURUN.
ile birlikte HttpRequestMessage
rastgele bilgiler geçirmek için özelliğini kullanabilirsiniz HttpRequestMessage.Options .
✔️ Kapsamla ilgili tüm (örneğin, kimlik doğrulaması) mantığını , tarafından IHttpClientFactory
oluşturulmamış ayrı DelegatingHandler
bir şekilde kapsülledikten sonra bunu kullanarak -created işleyicisini IHttpClientFactory
sarmalayın.
olmadan yalnızca bir HttpMessageHandler
oluşturmak için kayıtlı adlandırılmış herhangi bir istemciyi çağırIHttpMessageHandlerFactory.CreateHandler.HttpClient
Bu durumda, birleştirilmiş işleyiciyi kullanarak kendiniz bir HttpClient
örnek oluşturmanız gerekir. GitHub'da bu geçici çözüm için tam olarak çalıştırılabilir bir örnek bulabilirsiniz.
Daha fazla bilgi için yönergelerdeki IHttpClientFactory'de IHttpClientFactory
İleti İşleyici Kapsamları bölümüne bakın.
HttpClient
DNS değişikliklerine saygı göstermez
Kullanılsa IHttpClientFactory
bile eski DNS sorununa isabet etmek mümkündür. Bu durum genellikle bir HttpClient
örneğin bir Singleton
hizmette yakalanması veya genel olarak belirtilenden HandlerLifetime
daha uzun bir süre boyunca bir yerde depolanması durumunda gerçekleşebilir. HttpClient
ayrıca, ilgili yazılan istemci bir tekil tarafından yakalanırsa yakalanır.
❌tarafından IHttpClientFactory
oluşturulan örnekleri uzun süreler boyunca önbelleğe HttpClient
ALMAYIN.
❌Yazılan istemci örneklerini hizmetlere Singleton
EKLEMEYİN.
✔️ Bir istemciden IHttpClientFactory
zamanında veya her ihtiyaç duyduğunuzda istekte bulunmayı GÖZ ÖNÜNDE BULUNDURUN. Fabrika tarafından oluşturulan istemcilerin atılması güvenlidir.
HttpClient
tarafından IHttpClientFactory
oluşturulan örneklerin kısa ömürlü olması amaçlanmıştır.
İşleyicilerin DNS değişikliklerine tepki vermelerini sağlamak için
IHttpClientFactory
yaşam süreleri dolduğunda geri dönüşüm ve yeniden oluşturmaHttpMessageHandler
gereklidir.HttpClient
oluşturulurken belirli bir işleyici örneğine bağlıdır, bu nedenle istemcinin güncelleştirilmiş işleyiciyi edineceğinden emin olmak için yeniHttpClient
örneklerin zamanında istenmesi gerekir.Fabrika tarafından oluşturulan bu tür
HttpClient
örneklerin atılması yuva tükenmesine yol açmaz, bunun atılması, 'nin atılmasıHttpMessageHandler
tetiklenmez.IHttpClientFactory
örnekleri oluşturmakHttpClient
için kullanılan kaynakları izler ve atar. ÖrneğinHttpMessageHandler
kullanım ömrü dolmaya başlar ve kullanım süresi dolmaz.HttpClient
Yazılan istemcilerin de kısa ömürlü olması amaçlanır ve oluşturucuya bir HttpClient
örnek eklendiğinden, yazılan istemci ömrünü paylaşır.
Daha fazla bilgi için yönergelerdeki HttpClient
Tekli hizmetlerde yaşam süresi yönetimi ve Yazılan istemcilerden kaçınma bölümlerine IHttpClientFactory
bakın.
HttpClient
çok fazla yuva kullanıyor
Kullanılsa IHttpClientFactory
bile, belirli bir kullanım senaryosunda yuva tükenme sorununa isabet etmek mümkündür. Varsayılan olarak, HttpClient
eşzamanlı istek sayısını sınırlamaz. Aynı anda çok sayıda HTTP/1.1 isteği başlatılırsa, havuzda boş bağlantı olmadığından ve sınır ayarlanmadığından her biri yeni bir HTTP bağlantı girişimi tetikler.
❌ Sınırları belirtmeden aynı anda çok sayıda HTTP/1.1 isteği başlatmaYIN.
✔️ Bunu birincil işleyici olarak kullanıyorsanız, makul bir değere ayarlamayı HttpClientHandler.MaxConnectionsPerServer (veya SocketsHttpHandler.MaxConnectionsPerServer) GÖZ ÖNÜNDE BULUNDURUN. Bu sınırların yalnızca belirli işleyici örneği için geçerli olduğunu unutmayın.
✔️ Tek bir TCP bağlantısı üzerinden isteklerin birden çok katına alınmasına izin veren HTTP/2 kullanmayı göz önünde bulundurun.
Yazılan istemci yanlış HttpClient
eklenmiş
Türlenmiş bir istemciye beklenmeyen HttpClient
bir eklemenin mümkün olduğu çeşitli durumlar olabilir. Çoğu zaman, di tasarımına göre bir hizmetin sonraki kayıtları öncekini geçersiz kılacağından, kök neden hatalı bir yapılandırmada olur.
Yazılan istemciler adlandırılmış istemcileri "arka planda" kullanır: yazılan bir istemciyi örtük olarak kaydeder ve adlandırılmış bir istemciye bağlar. açıkça belirtilmediği sürece istemci adı türüne TClient
ayarlanır. Aşırı yüklemeler kullanılırsaAddHttpClient<TClient,TImplementation>
, bu çiftten TClient,TImplementation
ilk kez kullanılır.
Bu nedenle, türü belirlenmiş bir istemciyi kaydetmek iki ayrı işlem yapar:
- Adlandırılmış bir istemciyi kaydeder (basit bir varsayılan örnekte, adı olur
typeof(TClient).Name
). - veya
TClient,TImplementation
öğesini kullanarakTClient
birTransient
hizmeti kaydeder.
Aşağıdaki iki deyim teknik olarak aynıdır:
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
Basit bir durumda, aşağıdakine benzer olacaktır:
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))));
Yazılan ve adlandırılmış istemciler arasındaki bağlantının nasıl bozulabileceğine ilişkin aşağıdaki örnekleri göz önünde bulundurun.
Yazılan istemci ikinci kez kaydedilir
❌Yazılan istemciyi ayrı kaydetmeYIN; zaten çağrı tarafından AddHttpClient<T>
otomatik olarak kaydedilir.
Yazılan bir istemci yanlışlıkla düz Geçici hizmet olarak ikinci kez kaydedilirse, bu, tarafından HttpClientFactory
eklenen kaydın üzerine yazılır ve adlandırılmış istemcinin bağlantısını bozar. Yapılandırılmamış HttpClient
bir istemci bunun yerine türü belirtilen istemciye eklendiğinden yapılandırması kaybolur gibi HttpClient
bildirimde bulunur.
Özel durum oluşturmak yerine "yanlış" HttpClient
kullanılması kafa karıştırıcı olabilir. Bunun nedeni, en temel HttpClientFactory
kullanım senaryosunu etkinleştirmek için "varsayılan" yapılandırılmamış HttpClient
olan (string.Empty
) adlı istemcinin Options.DefaultName düz Geçici hizmet olarak kaydedilmesidir. Bu nedenle, bağlantı bozulur ve yazılan istemci sıradan bir hizmet haline geldikten sonra, bu "varsayılan" HttpClient
doğal olarak ilgili oluşturucu parametresine eklenir.
Farklı türdeki istemciler ortak bir arabirime kaydedilir
İki farklı türe sahip istemcinin ortak bir arabirimde kayıtlı olması durumunda ikisi de aynı adlandırılmış istemciyi yeniden kullanabilir. Bu, ikinci adlandırılmış istemciyi "yanlış" olarak eklenen ilk yazılan istemci gibi görünebilir.
❌ Adı açıkça belirtmeden tek bir arabirimde birden çok türe bağlı istemci kaydetmeYİN.
✔️ Adlandırılmış istemciyi ayrı ayrı kaydetmeyi ve yapılandırmayı ve ardından çağrıda AddHttpClient<T>
adı belirterek veya adlandırılmış istemci kurulumu sırasında arayarak AddTypedClient
bunu bir veya birden çok türü belirlenmiş istemciye bağlamayı göz önünde bulundurun.
Tasarım gereği, aynı ada sahip adlandırılmış bir istemciyi birkaç kez kaydedip yapılandırmak, yapılandırma eylemlerini var olan istemciler listesine ekler. Bu davranışı HttpClientFactory
belirgin olmayabilir, ancak Seçenekler deseni ve gibi Configureyapılandırma API'leri tarafından kullanılan yaklaşımla aynıdır.
Bu çoğunlukla gelişmiş işleyici yapılandırmaları için yararlıdır; örneğin, harici olarak tanımlanan adlandırılmış bir istemciye özel işleyici ekleme veya testler için birincil işleyiciyle alay etme, ancak örnek yapılandırması için HttpClient
de çalışır. Örneğin, aşağıdaki üç örnek aynı şekilde yapılandırılmış bir HttpClient
sonuçla sonuçlanır (hem hem de BaseAddress
DefaultRequestHeaders
ayarlanır):
// 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"));
Bu, türü belirlenmiş bir istemciyi zaten tanımlanmış adlandırılmış bir istemciye bağlamayı ve ayrıca birkaç türü belirlenmiş istemciyi tek bir adlandırılmış istemciye bağlamayı sağlar. Bir parametreye sahip name
aşırı yüklemeler kullanıldığında daha belirgindir:
services.AddHttpClient("LogClient", c => c.BaseAddress = new Uri(LogServerAddress));
services.AddHttpClient<FooLogger>("LogClient");
services.AddHttpClient<BarLogger>("LogClient");
Adlandırılmış istemci yapılandırması sırasında çağrılarak AddTypedClient da aynı şey elde edilebilir:
services.AddHttpClient("LogClient", c => c.BaseAddress = new Uri(LogServerAddress))
.AddTypedClient<FooLogger>()
.AddTypedClient<BarLogger>();
Ancak, aynı adlandırılmış istemciyi yeniden kullanmak istemiyorsanız, ancak yine de istemcileri aynı arabirime kaydetmek istiyorsanız, bunlar için açıkça farklı adlar belirterek bunu yapabilirsiniz:
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"));