Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Yaygın
Bu makalede, kullanarak örnek oluştururken IHttpClientFactoryHttpClient 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.
IHttpClientFactoryayrıca yuva tükenmesi ve DNS değişikliklerini kaybetme gibi sorunları önlemek için ve HttpClient örneklerinin HttpMessageHandler 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 HttpMessageHandlereriş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 IHttpClientFactorybilinen 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
HttpMessageHandlerkullanı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 HttpRequestMessagerastgele bilgiler geçirmek için özelliğini kullanabilirsiniz HttpRequestMessage.Options .
✔️ Kapsamla ilgili tüm (örneğin, kimlik doğrulaması) mantığını , tarafından DelegatingHandleroluşturulmamışbir şekilde kapsülledikten sonra bunu kullanarak -created işleyicisini IHttpClientFactorysarmalayın.
olmadan yalnızca bir HttpMessageHandler oluşturmak için kayıtlı HttpClientIHttpMessageHandlerFactory.CreateHandler 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'deIHttpClientFactory İ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 HandlerLifetimedaha 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 HttpClient oluşturulan örnekleri uzun süreler boyunca önbelleğe IHttpClientFactory ALMAYIN.
❌Yazılan istemci.
✔️ 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.
HttpClienttarafı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
HttpMessageHandleryaşam süreleri dolduğunda bunları geri dönüştürmek ve yeniden oluşturmakIHttpClientFactoryçok önemlidir.HttpClientoluş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ı.IHttpClientFactoryörnekleri oluşturmakHttpClientiçin kullanılan kaynakları izler ve atar. ÖrneğinHttpMessageHandlerkullanı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 TClientayarlanır.
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
Transientöğesini kullanarakTClientbirTClient,TImplementationhizmeti 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 IHttpClientFactoryeklenen 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 istemciyeHttpClientyapılandırması kaybolur gibi bildirimde bulunur.
Özel durum oluşturmak yerine "yanlış" HttpClient kullanılması kafa karıştırıcı olabilir. Bunun nedeni, en temel HttpClient kullanım senaryosunu etkinleştirmek için "varsayılan" yapılandırılmamış Options.DefaultName olan (string.Empty) adlı istemcinin IHttpClientFactory 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 adı belirterek veya adlandırılmış istemci kurulumu sırasında arayarak AddHttpClient<T> bunu bir veya birden çok AddTypedClient 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ışı IHttpClientFactory belirgin olmayabilir, ancak Seçenekler desenikullanı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ıHttpClientbir sonuçla sonuçlanır (hem hem de BaseAddressDefaultRequestHeaders 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.
name parametresi kullanıldığında aşırı yüklemeler daha belirgin hale gelir.
services.AddHttpClient("LogClient", c => c.BaseAddress = new Uri(LogServerAddress));
services.AddHttpClient<FooLogger>("LogClient");
services.AddHttpClient<BarLogger>("LogClient");
Adlandırılmış istemciAddTypedClientçağrılarak 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"));