ASP.NET Core'da IHttpClientFactory kullanarak HTTP isteklerinde bulunma
Uyarı
ASP.NET Core'un bu sürümü artık desteklenmiyor. Daha fazla bilgi için bkz . .NET ve .NET Core Destek İlkesi. Geçerli sürüm için bu makalenin .NET 8 sürümüne bakın.
Kirk Larkin, Steve Gordon, Glenn Condron ve Ryan Nowak tarafından.
, IHttpClientFactory bir uygulamada örnekleri yapılandırmak ve oluşturmak HttpClient için kaydedilebilir ve kullanılabilir. IHttpClientFactory
aşağıdaki avantajları sunar:
- Mantıksal
HttpClient
örneklerini adlandırmak ve yapılandırmak için merkezi bir konum sağlar. Örneğin github adlı bir istemci kaydedilebilir ve GitHub'a erişecek şekilde yapılandırılabilir. Varsayılan istemci genel erişim için kaydedilebilir. - içindeki işleyicileri temsilci olarak atama yoluyla giden ara yazılım kavramını uyumlu hale getirir
HttpClient
. içinde işleyicileri temsilci olarak atama avantajından yararlanmak için Polly tabanlı ara yazılım için uzantılarHttpClient
sağlar. - Temel
HttpClientMessageHandler
alınan örneklerin havuzunu ve ömrünü yönetir. Otomatik yönetim, yaşam süreleri el ile yönetildiğindeHttpClient
oluşan yaygın DNS (Etki Alanı Adı Sistemi) sorunlarını önler. - Fabrika tarafından oluşturulan istemciler aracılığıyla gönderilen tüm istekler için yapılandırılabilir bir günlük deneyimi (aracılığıyla
ILogger
) ekler.
Bu konu sürümündeki örnek kod, HTTP yanıtlarında döndürülen JSON içeriğini seri durumdan çıkarmak için kullanır System.Text.Json . ve ReadAsAsync<T>
kullanan Json.NET
örnekler için sürüm seçiciyi kullanarak bu konunun 2.x sürümünü seçin.
Tüketim desenleri
Bir uygulamada kullanılabilecek çeşitli yollar vardır IHttpClientFactory
:
En iyi yaklaşım, uygulamanın gereksinimlerine bağlıdır.
Temel kullanım
içinde Program.cs
arayarak AddHttpClient
kaydolunIHttpClientFactory
:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddHttpClient();
bağımlılık IHttpClientFactory
ekleme (DI) kullanılarak istenebilir. Aşağıdaki kod örnek HttpClient
oluşturmak için kullanırIHttpClientFactory
:
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);
}
}
}
IHttpClientFactory
Yukarıdaki örnekte like kullanmak, mevcut bir uygulamayı yeniden düzenlemenin iyi bir yoludur. Nasıl kullanıldığı üzerinde HttpClient
hiçbir etkisi yoktur. Mevcut bir uygulamada örneklerin oluşturulduğu yerlerde HttpClient
, bu oluşumları çağrısıyla CreateClientdeğiştirin.
Adlandırılmış istemciler
Adlandırılmış istemciler şu durumlarda iyi bir seçimdir:
- Uygulama için birçok farklı kullanım
HttpClient
gerekir. - Çoğu
HttpClient
farklı yapılandırmaya sahiptir.
içinde kaydı Program.cs
sırasında adlandırılmış bir için HttpClient
yapılandırma belirtin:
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");
});
İstemcinin yapılandırıldığı önceki kodda:
- temel adresi
https://api.github.com/
. - GitHub API'siyle çalışmak için iki üst bilgi gerekir.
CreateClient
Her seferinde CreateClient çağrılır:
- Yeni bir örneği
HttpClient
oluşturulur. - Yapılandırma eylemi çağrılır.
Adlandırılmış istemci oluşturmak için adını içine geçirin 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);
}
}
}
Yukarıdaki kodda isteğin bir konak adı belirtmesi gerekmez. İstemci için yapılandırılan temel adres kullanıldığından kod yalnızca yolu geçirebilir.
Yazılan istemciler
Yazılan istemciler:
- Dizeleri anahtar olarak kullanmaya gerek kalmadan adlandırılmış istemcilerle aynı özellikleri sağlayın.
- İstemcileri kullanırken IntelliSense ve derleyici yardımı sağlar.
- Belirli
HttpClient
bir öğesini yapılandırmak ve ile etkileşime geçmek için tek bir konum sağlayın. Örneğin, tek bir türlenmiş istemci kullanılabilir:- Tek bir arka uç uç noktası için.
- Uç noktayla ilgili tüm mantığı kapsüllemek için.
- DI ile çalışın ve uygulamada gerektiğinde eklenebilir.
Yazılan istemci, oluşturucusunda bir HttpClient
parametreyi kabul eder:
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");
}
Önceki kodda:
- Yapılandırma türü belirtilen istemciye taşınır.
- Sağlanan
HttpClient
örnek özel bir alan olarak depolanır.
İşlevselliği kullanıma sunan HttpClient
API'ye özgü yöntemler oluşturulabilir. Örneğin yöntemi, GetAspNetCoreDocsBranches
docs GitHub dallarını almak için kodu kapsüller.
Aşağıdaki kod, türü belirtilen istemci sınıfını GitHubService
kaydetmek için öğesini Program.cs
çağırırAddHttpClient:
builder.Services.AddHttpClient<GitHubService>();
Yazılan istemci, DI ile geçici olarak kaydedilir. Önceki kodda geçici AddHttpClient
bir hizmet olarak kaydeder GitHubService
. Bu kayıt, aşağıdakiler için bir fabrika yöntemi kullanır:
HttpClient
örneği oluşturun.- örneğini
GitHubService
oluşturucusunda geçirerek bir örneğiHttpClient
oluşturun.
Yazılan istemci doğrudan eklenebilir ve kullanılabilir:
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)
{
// ...
}
}
}
Türü belirtilmiş bir istemcinin yapılandırması, türü belirtilmiş istemcinin oluşturucusunda değil, içinde kaydı Program.cs
sırasında da belirtilebilir:
builder.Services.AddHttpClient<GitHubService>(httpClient =>
{
httpClient.BaseAddress = new Uri("https://api.github.com/");
// ...
});
Oluşturulan istemciler
IHttpClientFactory
Yeniden Sığdır gibi üçüncü taraf kitaplıklarla birlikte kullanılabilir. Refit, .NET için bir REST kitaplıktır. API'leri REST canlı arabirimlere dönüştürür. Dış HTTP çağrılarını yapmak için kullanan HttpClient
bir arabirimin dinamik uygulamasını oluşturmak için çağrısı AddRefitClient
yapın.
Özel arabirim dış API'yi temsil eder:
public interface IGitHubClient
{
[Get("/repos/dotnet/AspNetCore.Docs/branches")]
Task<IEnumerable<GitHubBranch>> GetAspNetCoreDocsBranchesAsync();
}
Dinamik uygulamayı oluşturmak için çağrısı AddRefitClient
yapın ve ardından temel alınan HttpClient
öğesini yapılandırmak için çağrısı ConfigureHttpClient
yapın:
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");
});
dinamik uygulamasına IGitHubClient
erişmek için DI kullanın:
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)
{
// ...
}
}
}
POST, PUT ve DELETE istekleri yapma
Yukarıdaki örneklerde, tüm HTTP istekleri GET HTTP fiilini kullanır. HttpClient
ayrıca aşağıdakiler dahil olmak üzere diğer HTTP fiillerini de destekler:
- POST
- PUT
- SİL
- PATCH
Desteklenen HTTP fiillerinin tam listesi için bkz HttpMethod. .
Aşağıdaki örnekte BIR HTTP POST isteğinin nasıl yapıldığını gösterilmektedir:
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();
}
Yukarıdaki kodda CreateItemAsync
yöntemi:
- parametresini
TodoItem
kullanarakSystem.Text.Json
JSON'a serileştirir. - HTTP isteğinin StringContent gövdesinde göndermek üzere serileştirilmiş JSON'ı paketlemek için bir örneği oluşturur.
- JSON içeriğini belirtilen URL'ye göndermek için çağrılar PostAsync . Bu, HttpClient.BaseAddress'e eklenen göreli bir URL'dir.
- Yanıt durum kodu başarıyı göstermiyorsa özel durum oluşturma çağrıları EnsureSuccessStatusCode .
HttpClient
ayrıca diğer içerik türlerini de destekler. Örneğin, MultipartContent ve StreamContent. Desteklenen içeriğin tam listesi için bkz HttpContent. .
Aşağıdaki örnekte bir HTTP PUT isteği gösterilmektedir:
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();
}
Yukarıdaki kod POST örneğine benzer. SaveItemAsync
yöntemi yerine PostAsync
öğesini çağırırPutAsync.
Aşağıdaki örnekte bir HTTP DELETE isteği gösterilmektedir:
public async Task DeleteItemAsync(long itemId)
{
using var httpResponseMessage =
await _httpClient.DeleteAsync($"/api/TodoItems/{itemId}");
httpResponseMessage.EnsureSuccessStatusCode();
}
Önceki kodda DeleteItemAsync
yöntemi öğesini çağırır DeleteAsync. HTTP DELETE istekleri genellikle gövde içermediğinden, DeleteAsync
yöntemi bir örneğini HttpContent
kabul eden bir aşırı yükleme sağlamaz.
ile HttpClient
farklı HTTP fiilleri kullanma hakkında daha fazla bilgi edinmek için bkz HttpClient. .
Giden istek ara yazılımı
HttpClient
, giden HTTP istekleri için birbirine bağlanabilen işleyicileri temsilci olarak belirleme kavramına sahiptir. IHttpClientFactory
:
- Adlandırılmış her istemci için uygulanacak işleyicilerin tanımlanmasını basitleştirir.
- Giden istek ara yazılım işlem hattı oluşturmak için birden çok işleyicinin kaydını ve zincirini destekler. Bu işleyicilerin her biri giden istek öncesinde ve sonrasında iş gerçekleştirebilir. Bu desen:
- ASP.NET Core'daki gelen ara yazılım işlem hattına benzer.
- HTTP istekleriyle ilgili çapraz kesme sorunlarını yönetmek için aşağıdakiler gibi bir mekanizma sağlar:
- önbelleğe alma
- hata işleme
- Seri -leştirme
- günlüğe kaydetme
Temsilci işleyicisi oluşturmak için:
- 'den DelegatingHandlertüretilir.
- öğesini geçersiz kılın SendAsync. İsteği işlem hattındaki bir sonraki işleyiciye geçirmeden önce kodu yürütebilirsiniz:
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);
}
}
Yukarıdaki kod, üst bilginin istekte olup olmadığını X-API-KEY
denetler. Eksikse X-API-KEY
, BadRequest döndürülür.
ile Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandleriçin HttpClient
yapılandırmaya birden fazla işleyici eklenebilir:
builder.Services.AddTransient<ValidateHeaderHandler>();
builder.Services.AddHttpClient("HttpMessageHandler")
.AddHttpMessageHandler<ValidateHeaderHandler>();
Yukarıdaki kodda ValidateHeaderHandler
, DI ile kaydedilir. Kaydedildikten sonra, AddHttpMessageHandler işleyici için türü geçirerek çağrılabilir.
Birden çok işleyici yürütülmeleri gereken sırayla kaydedilebilir. Her işleyici, son HttpClientHandler
isteği yürütene kadar sonraki işleyiciyi sarmalar:
builder.Services.AddTransient<SampleHandler1>();
builder.Services.AddTransient<SampleHandler2>();
builder.Services.AddHttpClient("MultipleHttpMessageHandlers")
.AddHttpMessageHandler<SampleHandler1>()
.AddHttpMessageHandler<SampleHandler2>();
Yukarıdaki kodda, SampleHandler1
önce, önce SampleHandler2
öğesini çalıştırır.
Giden istek ara yazılımında DI kullanma
IHttpClientFactory
Yeni bir temsilci seçme işleyicisi oluşturduğunda, işleyicinin oluşturucu parametrelerini yerine getirmek için DI kullanır. IHttpClientFactory
her işleyici için ayrı bir DI kapsamı oluşturur ve bu da bir işleyici kapsamlı bir hizmet tükettiğinde şaşırtıcı davranışlara yol açabilir.
Örneğin, bir görevi tanımlayıcısı OperationId
olan bir işlem olarak temsil eden aşağıdaki arabirimi ve uygulamasını göz önünde bulundurun:
public interface IOperationScoped
{
string OperationId { get; }
}
public class OperationScoped : IOperationScoped
{
public string OperationId { get; } = Guid.NewGuid().ToString()[^4..];
}
Adından da anlaşılacağı gibi, IOperationScoped
kapsamlı bir yaşam süresi kullanılarak DI'ye kaydedilir:
builder.Services.AddScoped<IOperationScoped, OperationScoped>();
Aşağıdaki temsilci işleyicisi, giden isteğin X-OPERATION-ID
üst bilgisini ayarlamak için kullanır ve kullanırIOperationScoped
:
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);
}
}
İndirmede HttpRequestsSample
sayfasına gidin /Operation
ve sayfayı yenileyin. İstek kapsamı değeri her istek için değişir, ancak işleyici kapsam değeri yalnızca 5 saniyede bir değişir.
İşleyiciler herhangi bir kapsamın hizmetlerine bağlı olabilir. İşleyicilerin bağımlı olduğu hizmetler, işleyici atıldığında atılır.
İstek başına durumu ileti işleyicileriyle paylaşmak için aşağıdaki yaklaşımlardan birini kullanın:
- kullanarak HttpRequestMessage.Optionsişleyiciye veri geçirme.
- Geçerli isteğe erişmek için kullanın IHttpContextAccessor .
- Verileri geçirmek için özel AsyncLocal<T> bir depolama nesnesi oluşturun.
Polly tabanlı işleyicileri kullanma
IHttpClientFactory
Üçüncü taraf kitaplığı Polly ile tümleşir. Polly, .NET için kapsamlı bir dayanıklılık ve geçici hata işleme kitaplığıdır. Geliştiricilerin Yeniden Deneme, Devre Kesici, Zaman Aşımı, Bulkhead Yalıtımı ve Geri Dönüş gibi ilkeleri akıcı ve iş parçacığı açısından güvenli bir şekilde ifade etmesine olanak tanır.
Yapılandırılmış HttpClient
örneklerle Polly ilkelerinin kullanımını etkinleştirmek için uzantı yöntemleri sağlanır. Polly uzantıları, polly tabanlı işleyicilerin istemcilere eklenmesini destekler. Polly, Microsoft.Extensions.Http.Polly NuGet paketini gerektirir.
Geçici hataları işleme
Hatalar genellikle dış HTTP çağrıları geçici olduğunda oluşur. AddTransientHttpErrorPolicy geçici hataları işlemek için bir ilkenin tanımlanmasına izin verir. ile AddTransientHttpErrorPolicy
yapılandırılan ilkeler aşağıdaki yanıtları işler:
- HttpRequestException
- HTTP 5xx
- HTTP 408
AddTransientHttpErrorPolicy
olası bir PolicyBuilder
geçici hatayı temsil eden hataları işlemek için yapılandırılmış bir nesneye erişim sağlar:
builder.Services.AddHttpClient("PollyWaitAndRetry")
.AddTransientHttpErrorPolicy(policyBuilder =>
policyBuilder.WaitAndRetryAsync(
3, retryNumber => TimeSpan.FromMilliseconds(600)));
Önceki kodda bir WaitAndRetryAsync
ilke tanımlanmıştır. Başarısız istekler, denemeler arasında 600 ms gecikmeyle üç kez yeniden denendi.
İlkeleri dinamik olarak seçme
Polly tabanlı işleyiciler eklemek için uzantı yöntemleri sağlanır. Örneğin, AddPolicyHandler. Aşağıdaki AddPolicyHandler
aşırı yükleme, hangi ilkenin uygulanacağını belirlemek için isteği inceler:
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);
Yukarıdaki kodda, giden istek bir HTTP GET ise, 10 saniyelik bir zaman aşımı uygulanır. Diğer herhangi bir HTTP yöntemi için 30 saniyelik bir zaman aşımı kullanılır.
Birden çok Polly işleyicisi ekleme
Polly ilkelerini iç içe yerleştirme yaygın olarak görülür:
builder.Services.AddHttpClient("PollyMultiple")
.AddTransientHttpErrorPolicy(policyBuilder =>
policyBuilder.RetryAsync(3))
.AddTransientHttpErrorPolicy(policyBuilder =>
policyBuilder.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
Yukarıdaki örnekte:
- İki işleyici eklenir.
- İlk işleyici, yeniden deneme ilkesi eklemek için kullanır AddTransientHttpErrorPolicy . Başarısız istekler en fazla üç kez yeniden deneniyor.
- İkinci
AddTransientHttpErrorPolicy
çağrı bir devre kesici ilkesi ekler. 5 başarısız girişim sırayla gerçekleşirse 30 saniye boyunca daha fazla dış istek engellenir. Devre kesici ilkeleri durum bilgisi vardır. Bu istemci aracılığıyla yapılan tüm çağrılar aynı devre durumunu paylaşır.
Polly kayıt defterinden ilke ekleme
Düzenli olarak kullanılan ilkeleri yönetmeye yönelik bir yaklaşım, bunları bir kez tanımlamak ve ile PolicyRegistry
kaydetmektir. Örneğin:
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");
Önceki kodda:
- polly kayıt defterine iki ilke
Regular
veLong
eklenir. - AddPolicyHandlerFromRegistry tek tek adlandırılmış istemcileri Polly kayıt defterinden bu ilkeleri kullanacak şekilde yapılandırıyor.
ve Polly tümleştirmeleri hakkında IHttpClientFactory
daha fazla bilgi için Polly wiki'sine bakın.
HttpClient ve yaşam süresi yönetimi
üzerinde IHttpClientFactory
her CreateClient
çağrıldığında yeni HttpClient
bir örnek döndürülür. HttpMessageHandler Adlandırılmış istemci başına bir oluşturulur. Fabrika, örneklerin yaşam ömrünü HttpMessageHandler
yönetir.
IHttpClientFactory
HttpMessageHandler
kaynak tüketimini azaltmak için fabrika tarafından oluşturulan örnekleri havuza alır. HttpMessageHandler
Yaşam süresi dolmadıysa, yeni HttpClient
bir örnek oluşturulurken bir örnek havuzdan yeniden kullanılabilir.
İşleyicilerin havuzu, her işleyici genellikle kendi temel HTTP bağlantılarını yönettiğinden tercih edilir. Gerekenden daha fazla işleyici oluşturmak bağlantı gecikmelerine neden olabilir. Bazı işleyiciler ayrıca bağlantıları süresiz olarak açık tutar ve bu da işleyicinin DNS (Etki Alanı Adı Sistemi) değişikliklerine tepki vermesini engelleyebilir.
Varsayılan işleyici ömrü iki dakikadır. Varsayılan değer, adlandırılmış istemci temelinde geçersiz kılınabilir:
builder.Services.AddHttpClient("HandlerLifetime")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
HttpClient
örnekleri genellikle elden çıkarma gerektirmeyen .NET nesneleri olarak kabul edilebilir. Atma, giden istekleri iptal eder ve belirtilen HttpClient
örneğin çağrıldıktan Disposesonra kullanılamayabileceğini garanti eder. IHttpClientFactory
örnekler tarafından HttpClient
kullanılan kaynakları izler ve atar.
Tek HttpClient
bir örneği uzun süre canlı tutmak, başlangıcından IHttpClientFactory
önce kullanılan yaygın bir desendir. bu düzen' e IHttpClientFactory
geçirildikten sonra gereksiz hale gelir.
IHttpClientFactory için alternatifler
DI özellikli bir uygulamada kullanmak IHttpClientFactory
aşağıdakilerden kaçınıyor:
- Örnekleri havuza alarak
HttpMessageHandler
kaynak tükenme sorunları. - Örnekleri düzenli aralıklarla döngüye alarak
HttpMessageHandler
eski DNS sorunları.
Önceki sorunları çözmek için uzun ömürlü SocketsHttpHandler bir örnek kullanmanın alternatif yolları vardır.
- Uygulamanın ne zaman başladığının
SocketsHttpHandler
bir örneğini oluşturun ve uygulamanın ömrü boyunca kullanın. - DNS yenileme sürelerine göre uygun bir değere yapılandırın PooledConnectionLifetime .
- Gerektiğinde kullanarak
new HttpClient(handler, disposeHandler: false)
örnekler oluşturunHttpClient
.
Yukarıdaki yaklaşımlar, benzer şekilde çözülen IHttpClientFactory
kaynak yönetimi sorunlarını çözer.
- Örnekler
SocketsHttpHandler
arasındaHttpClient
bağlantıları paylaşır. Bu paylaşım yuva tükenmesini önler. - Eski
SocketsHttpHandler
DNS sorunlarını önlemek içinPooledConnectionLifetime
bağlantıları göre döngüye alır.
Günlük Kaydı
Tüm istekler için kayıt günlüğü iletileri aracılığıyla IHttpClientFactory
oluşturulan istemciler. Varsayılan günlük iletilerini görmek için günlük yapılandırmasında uygun bilgi düzeyini etkinleştirin. İstek üst bilgilerinin günlüğe kaydedilmesi gibi ek günlükler yalnızca izleme düzeyinde eklenir.
Her istemci için kullanılan günlük kategorisi, istemcinin adını içerir. Örneğin MyNamedClient adlı bir istemci, "System.Net.Http.HttpClient kategorisine sahip iletileri günlüğe kaydeder.MyNamedClient. LogicalHandler". LogicalHandler ile son ekli iletiler, istek işleyicisi işlem hattının dışında gerçekleşir. İstekte, işlem hattındaki diğer işleyiciler işlemeden önce iletiler günlüğe kaydedilir. Yanıtta, diğer işlem hattı işleyicileri yanıtı aldıktan sonra iletiler günlüğe kaydedilir.
Günlük kaydı, istek işleyicisi işlem hattında da gerçekleşir. MyNamedClient örneğinde, bu iletiler "System.Net.Http.HttpClient" günlük kategorisiyle günlüğe kaydedilir.MyNamedClient. ClientHandler". İstek için bu, diğer tüm işleyiciler çalıştırıldıktan sonra ve istek gönderilmeden hemen önce gerçekleşir. Yanıtta, bu günlük işleyici işlem hattından geri geçmeden önce yanıtın durumunu içerir.
İşlem hattının dışında ve içinde günlüğe kaydetmeyi etkinleştirmek, diğer işlem hattı işleyicileri tarafından yapılan değişikliklerin denetlenebilmesini sağlar. Bu, istek üst bilgilerinde veya yanıt durum kodunda yapılan değişiklikleri içerebilir.
İstemcinin adını günlük kategorisine dahil etme, belirli adlandırılmış istemciler için günlük filtrelemeyi etkinleştirir.
HttpMessageHandler'ı yapılandırma
İstemci tarafından kullanılan iç HttpMessageHandler
öğesinin yapılandırmasını denetlemek gerekebilir.
IHttpClientBuilder
adlandırılmış veya yazılan istemciler eklenirken bir döndürülür. ConfigurePrimaryHttpMessageHandler Uzantı yöntemi bir temsilci tanımlamak için kullanılabilir. Temsilci, bu istemci tarafından kullanılan birincil HttpMessageHandler
değeri oluşturmak ve yapılandırmak için kullanılır:
builder.Services.AddHttpClient("ConfiguredHttpMessageHandler")
.ConfigurePrimaryHttpMessageHandler(() =>
new HttpClientHandler
{
AllowAutoRedirect = true,
UseDefaultCredentials = true
});
Tanımlama bilgileri
Havuza alınan HttpMessageHandler
örnekler, nesnelerin paylaşılmasıyla CookieContainer
sonuçlanır. Tahmin edilmeyen CookieContainer
nesne paylaşımı genellikle yanlış koda neden olur. Tanımlama bilgileri gerektiren uygulamalar için şunlardan birini göz önünde bulundurun:
- Otomatik cookie işlemeyi devre dışı bırakma
- Kaçın -arak
IHttpClientFactory
Otomatik cookie işlemeyi devre dışı bırakmak için çağrısıConfigurePrimaryHttpMessageHandler:
builder.Services.AddHttpClient("NoAutomaticCookies")
.ConfigurePrimaryHttpMessageHandler(() =>
new HttpClientHandler
{
UseCookies = false
});
Konsol uygulamasında IHttpClientFactory kullanma
Konsol uygulamasında projeye aşağıdaki paket başvurularını ekleyin:
Aşağıdaki örnekte:
- IHttpClientFactoryve
GitHubService
Genel Ana Bilgisayarın hizmet kapsayıcısında kayıtlıdır. GitHubService
DI'den istenir ve bu da örneğiniIHttpClientFactory
talep eder.GitHubService
, belgeleri GitHub dallarını almak için kullandığı bir örneğiniHttpClient
oluşturmak için kullanırIHttpClientFactory
.
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);
Üst bilgi yayma ara yazılımı
Üst bilgi yayma, http üst bilgilerini gelen istekten giden HttpClient
isteklere yaymak için ASP.NET Core ara yazılımıdır. Üst bilgi yayma özelliğini kullanmak için:
Microsoft.AspNetCore.HeaderPropagation paketini yükleyin.
HttpClient
ve ara yazılım işlem hattını içindeProgram.cs
yapılandırın:// 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();
Eklenen üst bilgileri içeren yapılandırılmış
HttpClient
örneği kullanarak giden istekler yapın.
Ek kaynaklar
Kirk Larkin, Steve Gordon, Glenn Condron ve Ryan Nowak tarafından.
, IHttpClientFactory bir uygulamada örnekleri yapılandırmak ve oluşturmak HttpClient için kaydedilebilir ve kullanılabilir. IHttpClientFactory
aşağıdaki avantajları sunar:
- Mantıksal
HttpClient
örneklerini adlandırmak ve yapılandırmak için merkezi bir konum sağlar. Örneğin github adlı bir istemci kaydedilebilir ve GitHub'a erişecek şekilde yapılandırılabilir. Varsayılan istemci genel erişim için kaydedilebilir. - içindeki işleyicileri temsilci olarak atama yoluyla giden ara yazılım kavramını uyumlu hale getirir
HttpClient
. içinde işleyicileri temsilci olarak atama avantajından yararlanmak için Polly tabanlı ara yazılım için uzantılarHttpClient
sağlar. - Temel
HttpClientMessageHandler
alınan örneklerin havuzunu ve ömrünü yönetir. Otomatik yönetim, yaşam süreleri el ile yönetildiğindeHttpClient
oluşan yaygın DNS (Etki Alanı Adı Sistemi) sorunlarını önler. - Fabrika tarafından oluşturulan istemciler aracılığıyla gönderilen tüm istekler için yapılandırılabilir bir günlük deneyimi (aracılığıyla
ILogger
) ekler.
Örnek kodu görüntüleme veya indirme (indirme).
Bu konu sürümündeki örnek kod, HTTP yanıtlarında döndürülen JSON içeriğini seri durumdan çıkarmak için kullanır System.Text.Json . ve ReadAsAsync<T>
kullanan Json.NET
örnekler için sürüm seçiciyi kullanarak bu konunun 2.x sürümünü seçin.
Tüketim desenleri
Bir uygulamada kullanılabilecek çeşitli yollar vardır IHttpClientFactory
:
En iyi yaklaşım, uygulamanın gereksinimlerine bağlıdır.
Temel kullanım
IHttpClientFactory
çağrılarak AddHttpClient
kaydedilebilir:
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.
bağımlılık IHttpClientFactory
ekleme (DI) kullanılarak istenebilir. Aşağıdaki kod örnek HttpClient
oluşturmak için kullanırIHttpClientFactory
:
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>();
}
}
}
IHttpClientFactory
Yukarıdaki örnekte like kullanmak, mevcut bir uygulamayı yeniden düzenlemenin iyi bir yoludur. Nasıl kullanıldığı üzerinde HttpClient
hiçbir etkisi yoktur. Mevcut bir uygulamada örneklerin oluşturulduğu yerlerde HttpClient
, bu oluşumları çağrısıyla CreateClientdeğiştirin.
Adlandırılmış istemciler
Adlandırılmış istemciler şu durumlarda iyi bir seçimdir:
- Uygulama için birçok farklı kullanım
HttpClient
gerekir. - Çoğu
HttpClient
farklı yapılandırmaya sahiptir.
Adlı bir ad HttpClient
için yapılandırma, kaydı Startup.ConfigureServices
sırasında belirtilebilir:
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");
});
İstemcinin yapılandırıldığı önceki kodda:
- temel adresi
https://api.github.com/
. - GitHub API'siyle çalışmak için iki üst bilgi gerekir.
CreateClient
Her seferinde CreateClient çağrılır:
- Yeni bir örneği
HttpClient
oluşturulur. - Yapılandırma eylemi çağrılır.
Adlandırılmış istemci oluşturmak için adını içine geçirin 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>();
}
}
}
Yukarıdaki kodda isteğin bir konak adı belirtmesi gerekmez. İstemci için yapılandırılan temel adres kullanıldığından kod yalnızca yolu geçirebilir.
Yazılan istemciler
Yazılan istemciler:
- Dizeleri anahtar olarak kullanmaya gerek kalmadan adlandırılmış istemcilerle aynı özellikleri sağlayın.
- İstemcileri kullanırken IntelliSense ve derleyici yardımı sağlar.
- Belirli
HttpClient
bir öğesini yapılandırmak ve ile etkileşime geçmek için tek bir konum sağlayın. Örneğin, tek bir türlenmiş istemci kullanılabilir:- Tek bir arka uç uç noktası için.
- Uç noktayla ilgili tüm mantığı kapsüllemek için.
- DI ile çalışın ve uygulamada gerektiğinde eklenebilir.
Yazılan istemci, oluşturucusunda bir HttpClient
parametreyi kabul eder:
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");
}
}
Önceki kodda:
- Yapılandırma türü belirtilen istemciye taşınır.
HttpClient
Nesnesi genel bir özellik olarak kullanıma sunulur.
İşlevselliği kullanıma sunan HttpClient
API'ye özgü yöntemler oluşturulabilir. Örneğin, GetAspNetDocsIssues
yöntemi açık sorunları almak için kodu kapsüller.
Aşağıdaki kod, türü belirtilen bir istemci sınıfını kaydetmek için öğesini Startup.ConfigureServices
çağırırAddHttpClient:
services.AddHttpClient<GitHubService>();
Yazılan istemci, DI ile geçici olarak kaydedilir. Önceki kodda geçici AddHttpClient
bir hizmet olarak kaydeder GitHubService
. Bu kayıt, aşağıdakiler için bir fabrika yöntemi kullanır:
HttpClient
örneği oluşturun.- örneğini
GitHubService
oluşturucusunda geçirerek bir örneğiHttpClient
oluşturun.
Yazılan istemci doğrudan eklenebilir ve kullanılabilir:
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>();
}
}
}
Türü belirtilmiş bir istemcinin yapılandırması, türü belirtilmiş istemcinin oluşturucusunda değil, içinde kayıt Startup.ConfigureServices
sırasında belirtilebilir:
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");
});
HttpClient
türü belirtilmiş bir istemci içinde kapsüllenebilir. Bunu bir özellik olarak ortaya çıkarmak yerine, örneği dahili olarak çağıran HttpClient
bir yöntem tanımlayın:
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);
}
}
Yukarıdaki kodda , HttpClient
özel bir alanda depolanır. erişimi HttpClient
genel GetRepos
yöntemiyle yapılır.
Oluşturulan istemciler
IHttpClientFactory
Yeniden Sığdır gibi üçüncü taraf kitaplıklarla birlikte kullanılabilir. Refit, .NET için bir REST kitaplıktır. API'leri REST canlı arabirimlere dönüştürür. Arabirimin bir uygulaması, dış HTTP çağrılarını yapmak için kullanılarak HttpClient
kullanılarak dinamik olarak RestService
oluşturulur.
Dış API'yi ve yanıtını temsil etmek için bir arabirim ve yanıt tanımlanır:
public interface IHelloClient
{
[Get("/helloworld")]
Task<Reply> GetMessageAsync();
}
public class Reply
{
public string Message { get; set; }
}
Uygulamayı oluşturmak için Refit kullanılarak türü oluşturulmuş bir istemci eklenebilir:
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();
}
Tanımlı arabirim, DI ve Refit tarafından sağlanan uygulamayla gerektiğinde kullanılabilir:
[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();
}
}
POST, PUT ve DELETE istekleri yapma
Yukarıdaki örneklerde, tüm HTTP istekleri GET HTTP fiilini kullanır. HttpClient
ayrıca aşağıdakiler dahil olmak üzere diğer HTTP fiillerini de destekler:
- POST
- PUT
- SİL
- PATCH
Desteklenen HTTP fiillerinin tam listesi için bkz HttpMethod. .
Aşağıdaki örnekte BIR HTTP POST isteğinin nasıl yapıldığını gösterilmektedir:
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();
}
Yukarıdaki kodda CreateItemAsync
yöntemi:
- parametresini
TodoItem
kullanarakSystem.Text.Json
JSON'a serileştirir. Bu, serileştirme işlemini yapılandırmak için örneğini JsonSerializerOptions kullanır. - HTTP isteğinin StringContent gövdesinde göndermek üzere serileştirilmiş JSON'ı paketlemek için bir örneği oluşturur.
- JSON içeriğini belirtilen URL'ye göndermek için çağrılar PostAsync . Bu, HttpClient.BaseAddress'e eklenen göreli bir URL'dir.
- Yanıt durum kodu başarıyı göstermiyorsa özel durum oluşturma çağrısı EnsureSuccessStatusCode yapar.
HttpClient
ayrıca diğer içerik türlerini de destekler. Örneğin, MultipartContent ve StreamContent. Desteklenen içeriğin tam listesi için bkz HttpContent. .
Aşağıdaki örnekte bir HTTP PUT isteği gösterilmektedir:
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();
}
Yukarıdaki kod POST örneğine çok benzer. SaveItemAsync
yöntemi yerine PostAsync
öğesini çağırırPutAsync.
Aşağıdaki örnekte bir HTTP DELETE isteği gösterilmektedir:
public async Task DeleteItemAsync(long itemId)
{
using var httpResponse =
await _httpClient.DeleteAsync($"/api/TodoItems/{itemId}");
httpResponse.EnsureSuccessStatusCode();
}
Önceki kodda DeleteItemAsync
yöntemi öğesini çağırır DeleteAsync. HTTP DELETE istekleri genellikle gövde içermediğinden, DeleteAsync
yöntemi bir örneğini HttpContent
kabul eden bir aşırı yükleme sağlamaz.
ile HttpClient
farklı HTTP fiilleri kullanma hakkında daha fazla bilgi edinmek için bkz HttpClient. .
Giden istek ara yazılımı
HttpClient
, giden HTTP istekleri için birbirine bağlanabilen işleyicileri temsilci olarak belirleme kavramına sahiptir. IHttpClientFactory
:
- Adlandırılmış her istemci için uygulanacak işleyicilerin tanımlanmasını basitleştirir.
- Giden istek ara yazılım işlem hattı oluşturmak için birden çok işleyicinin kaydını ve zincirini destekler. Bu işleyicilerin her biri giden istek öncesinde ve sonrasında iş gerçekleştirebilir. Bu desen:
- ASP.NET Core'daki gelen ara yazılım işlem hattına benzer.
- HTTP istekleriyle ilgili çapraz kesme sorunlarını yönetmek için aşağıdakiler gibi bir mekanizma sağlar:
- önbelleğe alma
- hata işleme
- Seri -leştirme
- günlüğe kaydetme
Temsilci işleyicisi oluşturmak için:
- 'den DelegatingHandlertüretilir.
- öğesini geçersiz kılın SendAsync. İsteği işlem hattındaki bir sonraki işleyiciye geçirmeden önce kodu yürütebilirsiniz:
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);
}
}
Yukarıdaki kod, üst bilginin istekte olup olmadığını X-API-KEY
denetler. Eksikse X-API-KEY
, BadRequest döndürülür.
ile Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandleriçin HttpClient
yapılandırmaya birden fazla işleyici eklenebilir:
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.
Yukarıdaki kodda ValidateHeaderHandler
, DI ile kaydedilir. Kaydedildikten sonra, AddHttpMessageHandler işleyici için türü geçirerek çağrılabilir.
Birden çok işleyici yürütülmeleri gereken sırayla kaydedilebilir. Her işleyici, son HttpClientHandler
isteği yürütene kadar sonraki işleyiciyi sarmalar:
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>();
Giden istek ara yazılımında DI kullanma
IHttpClientFactory
Yeni bir temsilci seçme işleyicisi oluşturduğunda, işleyicinin oluşturucu parametrelerini yerine getirmek için DI kullanır. IHttpClientFactory
her işleyici için ayrı bir DI kapsamı oluşturur ve bu da bir işleyici kapsamlı bir hizmet tükettiğinde şaşırtıcı davranışlara yol açabilir.
Örneğin, bir görevi tanımlayıcısı OperationId
olan bir işlem olarak temsil eden aşağıdaki arabirimi ve uygulamasını göz önünde bulundurun:
public interface IOperationScoped
{
string OperationId { get; }
}
public class OperationScoped : IOperationScoped
{
public string OperationId { get; } = Guid.NewGuid().ToString()[^4..];
}
Adından da anlaşılacağı gibi, IOperationScoped
kapsamlı bir yaşam süresi kullanılarak DI'ye kaydedilir:
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();
}
Aşağıdaki temsilci işleyicisi, giden isteğin X-OPERATION-ID
üst bilgisini ayarlamak için kullanır ve kullanırIOperationScoped
:
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);
}
}
İndirme] bölümünde HttpRequestsSample
sayfaya /Operation
gidin ve sayfayı yenileyin. İstek kapsamı değeri her istek için değişir, ancak işleyici kapsam değeri yalnızca 5 saniyede bir değişir.
İşleyiciler herhangi bir kapsamın hizmetlerine bağlı olabilir. İşleyicilerin bağımlı olduğu hizmetler, işleyici atıldığında atılır.
İstek başına durumu ileti işleyicileriyle paylaşmak için aşağıdaki yaklaşımlardan birini kullanın:
- kullanarak HttpRequestMessage.Optionsişleyiciye veri geçirme.
- Geçerli isteğe erişmek için kullanın IHttpContextAccessor .
- Verileri geçirmek için özel AsyncLocal<T> bir depolama nesnesi oluşturun.
Polly tabanlı işleyicileri kullanma
IHttpClientFactory
Üçüncü taraf kitaplığı Polly ile tümleşir. Polly, .NET için kapsamlı bir dayanıklılık ve geçici hata işleme kitaplığıdır. Geliştiricilerin Yeniden Deneme, Devre Kesici, Zaman Aşımı, Bulkhead Yalıtımı ve Geri Dönüş gibi ilkeleri akıcı ve iş parçacığı açısından güvenli bir şekilde ifade etmesine olanak tanır.
Yapılandırılmış HttpClient
örneklerle Polly ilkelerinin kullanımını etkinleştirmek için uzantı yöntemleri sağlanır. Polly uzantıları, polly tabanlı işleyicilerin istemcilere eklenmesini destekler. Polly, Microsoft.Extensions.Http.Polly NuGet paketini gerektirir.
Geçici hataları işleme
Hatalar genellikle dış HTTP çağrıları geçici olduğunda oluşur. AddTransientHttpErrorPolicy geçici hataları işlemek için bir ilkenin tanımlanmasına izin verir. ile AddTransientHttpErrorPolicy
yapılandırılan ilkeler aşağıdaki yanıtları işler:
- HttpRequestException
- HTTP 5xx
- HTTP 408
AddTransientHttpErrorPolicy
olası bir PolicyBuilder
geçici hatayı temsil eden hataları işlemek için yapılandırılmış bir nesneye erişim sağlar:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<UnreliableEndpointCallerService>()
.AddTransientHttpErrorPolicy(p =>
p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));
// Remaining code deleted for brevity.
Önceki kodda bir WaitAndRetryAsync
ilke tanımlanmıştır. Başarısız istekler, denemeler arasında 600 ms gecikmeyle üç kez yeniden denendi.
İlkeleri dinamik olarak seçme
Polly tabanlı işleyiciler eklemek için uzantı yöntemleri sağlanır. Örneğin, AddPolicyHandler. Aşağıdaki AddPolicyHandler
aşırı yükleme, hangi ilkenin uygulanacağını belirlemek için isteği inceler:
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);
Yukarıdaki kodda, giden istek bir HTTP GET ise, 10 saniyelik bir zaman aşımı uygulanır. Diğer herhangi bir HTTP yöntemi için 30 saniyelik bir zaman aşımı kullanılır.
Birden çok Polly işleyicisi ekleme
Polly ilkelerini iç içe yerleştirme yaygın olarak görülür:
services.AddHttpClient("multiplepolicies")
.AddTransientHttpErrorPolicy(p => p.RetryAsync(3))
.AddTransientHttpErrorPolicy(
p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
Yukarıdaki örnekte:
- İki işleyici eklenir.
- İlk işleyici, yeniden deneme ilkesi eklemek için kullanır AddTransientHttpErrorPolicy . Başarısız istekler en fazla üç kez yeniden deneniyor.
- İkinci
AddTransientHttpErrorPolicy
çağrı bir devre kesici ilkesi ekler. 5 başarısız girişim sırayla gerçekleşirse 30 saniye boyunca daha fazla dış istek engellenir. Devre kesici ilkeleri durum bilgisi vardır. Bu istemci aracılığıyla yapılan tüm çağrılar aynı devre durumunu paylaşır.
Polly kayıt defterinden ilke ekleme
Düzenli olarak kullanılan ilkeleri yönetmeye yönelik bir yaklaşım, bunları bir kez tanımlamak ve ile PolicyRegistry
kaydetmektir.
Aşağıdaki kodda:
- "Normal" ve "uzun" ilkeler eklenir.
- AddPolicyHandlerFromRegistry kayıt defterinden "normal" ve "uzun" ilkeleri ekler.
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.
ve Polly tümleştirmeleri hakkında IHttpClientFactory
daha fazla bilgi için Polly wiki'sine bakın.
HttpClient ve yaşam süresi yönetimi
üzerinde IHttpClientFactory
her CreateClient
çağrıldığında yeni HttpClient
bir örnek döndürülür. HttpMessageHandler Adlandırılmış istemci başına bir oluşturulur. Fabrika, örneklerin yaşam ömrünü HttpMessageHandler
yönetir.
IHttpClientFactory
HttpMessageHandler
kaynak tüketimini azaltmak için fabrika tarafından oluşturulan örnekleri havuza alır. HttpMessageHandler
Yaşam süresi dolmadıysa, yeni HttpClient
bir örnek oluşturulurken bir örnek havuzdan yeniden kullanılabilir.
İşleyicilerin havuzu, her işleyici genellikle kendi temel HTTP bağlantılarını yönettiğinden tercih edilir. Gerekenden daha fazla işleyici oluşturmak bağlantı gecikmelerine neden olabilir. Bazı işleyiciler ayrıca bağlantıları süresiz olarak açık tutar ve bu da işleyicinin DNS (Etki Alanı Adı Sistemi) değişikliklerine tepki vermesini engelleyebilir.
Varsayılan işleyici ömrü iki dakikadır. Varsayılan değer, adlandırılmış istemci temelinde geçersiz kılınabilir:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("extendedhandlerlifetime")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
// Remaining code deleted for brevity.
HttpClient
örnekleri genellikle elden çıkarma gerektirmeyen .NET nesneleri olarak kabul edilebilir. Atma, giden istekleri iptal eder ve belirtilen HttpClient
örneğin çağrıldıktan Disposesonra kullanılamayabileceğini garanti eder. IHttpClientFactory
örnekler tarafından HttpClient
kullanılan kaynakları izler ve atar.
Tek HttpClient
bir örneği uzun süre canlı tutmak, başlangıcından IHttpClientFactory
önce kullanılan yaygın bir desendir. bu düzen' e IHttpClientFactory
geçirildikten sonra gereksiz hale gelir.
IHttpClientFactory için alternatifler
DI özellikli bir uygulamada kullanmak IHttpClientFactory
aşağıdakilerden kaçınıyor:
- Örnekleri havuza alarak
HttpMessageHandler
kaynak tükenme sorunları. - Örnekleri düzenli aralıklarla döngüye alarak
HttpMessageHandler
eski DNS sorunları.
Önceki sorunları çözmek için uzun ömürlü SocketsHttpHandler bir örnek kullanmanın alternatif yolları vardır.
- Uygulamanın ne zaman başladığının
SocketsHttpHandler
bir örneğini oluşturun ve uygulamanın ömrü boyunca kullanın. - DNS yenileme sürelerine göre uygun bir değere yapılandırın PooledConnectionLifetime .
- Gerektiğinde kullanarak
new HttpClient(handler, disposeHandler: false)
örnekler oluşturunHttpClient
.
Yukarıdaki yaklaşımlar, benzer şekilde çözülen IHttpClientFactory
kaynak yönetimi sorunlarını çözer.
- Örnekler
SocketsHttpHandler
arasındaHttpClient
bağlantıları paylaşır. Bu paylaşım yuva tükenmesini önler. - Eski
SocketsHttpHandler
DNS sorunlarını önlemek içinPooledConnectionLifetime
bağlantıları göre döngüye alır.
Tanımlama bilgileri
Havuza alınan HttpMessageHandler
örnekler, nesnelerin paylaşılmasıyla CookieContainer
sonuçlanır. Tahmin edilmeyen CookieContainer
nesne paylaşımı genellikle yanlış koda neden olur. Tanımlama bilgileri gerektiren uygulamalar için şunlardan birini göz önünde bulundurun:
- Otomatik cookie işlemeyi devre dışı bırakma
- Kaçın -arak
IHttpClientFactory
Otomatik cookie işlemeyi devre dışı bırakmak için çağrısıConfigurePrimaryHttpMessageHandler:
services.AddHttpClient("configured-disable-automatic-cookies")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
UseCookies = false,
};
});
Günlük Kaydı
Tüm istekler için kayıt günlüğü iletileri aracılığıyla IHttpClientFactory
oluşturulan istemciler. Varsayılan günlük iletilerini görmek için günlük yapılandırmasında uygun bilgi düzeyini etkinleştirin. İstek üst bilgilerinin günlüğe kaydedilmesi gibi ek günlükler yalnızca izleme düzeyinde eklenir.
Her istemci için kullanılan günlük kategorisi, istemcinin adını içerir. Örneğin MyNamedClient adlı bir istemci, "System.Net.Http.HttpClient kategorisine sahip iletileri günlüğe kaydeder.MyNamedClient. LogicalHandler". LogicalHandler ile son ekli iletiler, istek işleyicisi işlem hattının dışında gerçekleşir. İstekte, işlem hattındaki diğer işleyiciler işlemeden önce iletiler günlüğe kaydedilir. Yanıtta, diğer işlem hattı işleyicileri yanıtı aldıktan sonra iletiler günlüğe kaydedilir.
Günlük kaydı, istek işleyicisi işlem hattında da gerçekleşir. MyNamedClient örneğinde, bu iletiler "System.Net.Http.HttpClient" günlük kategorisiyle günlüğe kaydedilir.MyNamedClient. ClientHandler". İstek için bu, diğer tüm işleyiciler çalıştırıldıktan sonra ve istek gönderilmeden hemen önce gerçekleşir. Yanıtta, bu günlük işleyici işlem hattından geri geçmeden önce yanıtın durumunu içerir.
İşlem hattının dışında ve içinde günlüğe kaydetmeyi etkinleştirmek, diğer işlem hattı işleyicileri tarafından yapılan değişikliklerin denetlenebilmesini sağlar. Bu, istek üst bilgilerinde veya yanıt durum kodunda yapılan değişiklikleri içerebilir.
İstemcinin adını günlük kategorisine dahil etme, belirli adlandırılmış istemciler için günlük filtrelemeyi etkinleştirir.
HttpMessageHandler'ı yapılandırma
İstemci tarafından kullanılan iç HttpMessageHandler
öğesinin yapılandırmasını denetlemek gerekebilir.
IHttpClientBuilder
adlandırılmış veya yazılan istemciler eklenirken bir döndürülür. ConfigurePrimaryHttpMessageHandler Uzantı yöntemi bir temsilci tanımlamak için kullanılabilir. Temsilci, bu istemci tarafından kullanılan birincil HttpMessageHandler
değeri oluşturmak ve yapılandırmak için kullanılır:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("configured-inner-handler")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
AllowAutoRedirect = false,
UseDefaultCredentials = true
};
});
// Remaining code deleted for brevity.
Konsol uygulamasında IHttpClientFactory kullanma
Konsol uygulamasında projeye aşağıdaki paket başvurularını ekleyin:
Aşağıdaki örnekte:
- IHttpClientFactory, Genel Ana Bilgisayarın hizmet kapsayıcısında kayıtlıdır.
MyService
, hizmetten bir istemci fabrika örneği oluşturur ve bunu oluşturmakHttpClient
için kullanılır.HttpClient
bir web sayfasını almak için kullanılır.Main
hizmetinGetPage
yöntemini yürütmek için bir kapsam oluşturur ve web sayfası içeriğinin ilk 500 karakterini konsola yazar.
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}";
}
}
}
}
Üst bilgi yayma ara yazılımı
Üst bilgi yayma, http üst bilgilerini gelen istekten giden HTTP İstemcisi isteklerine yaymak için kullanılan bir ASP.NET Core ara yazılımıdır. Üst bilgi yayma özelliğini kullanmak için:
Ara yazılımı ve
HttpClient
içindeStartup
yapılandırın: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(); }); }
İstemci, giden isteklerde yapılandırılmış üst bilgileri içerir:
var client = clientFactory.CreateClient("MyForwardingClient"); var response = client.GetAsync(...);
Ek kaynaklar
Kirk Larkin, Steve Gordon, Glenn Condron ve Ryan Nowak tarafından.
, IHttpClientFactory bir uygulamada örnekleri yapılandırmak ve oluşturmak HttpClient için kaydedilebilir ve kullanılabilir. IHttpClientFactory
aşağıdaki avantajları sunar:
- Mantıksal
HttpClient
örneklerini adlandırmak ve yapılandırmak için merkezi bir konum sağlar. Örneğin github adlı bir istemci kaydedilebilir ve GitHub'a erişecek şekilde yapılandırılabilir. Varsayılan istemci genel erişim için kaydedilebilir. - içindeki işleyicileri temsilci olarak atama yoluyla giden ara yazılım kavramını uyumlu hale getirir
HttpClient
. içinde işleyicileri temsilci olarak atama avantajından yararlanmak için Polly tabanlı ara yazılım için uzantılarHttpClient
sağlar. - Temel
HttpClientMessageHandler
alınan örneklerin havuzunu ve ömrünü yönetir. Otomatik yönetim, yaşam süreleri el ile yönetildiğindeHttpClient
oluşan yaygın DNS (Etki Alanı Adı Sistemi) sorunlarını önler. - Fabrika tarafından oluşturulan istemciler aracılığıyla gönderilen tüm istekler için yapılandırılabilir bir günlük deneyimi (aracılığıyla
ILogger
) ekler.
Örnek kodu görüntüleme veya indirme (indirme).
Bu konu sürümündeki örnek kod, HTTP yanıtlarında döndürülen JSON içeriğini seri durumdan çıkarmak için kullanır System.Text.Json . ve ReadAsAsync<T>
kullanan Json.NET
örnekler için sürüm seçiciyi kullanarak bu konunun 2.x sürümünü seçin.
Tüketim desenleri
Bir uygulamada kullanılabilecek çeşitli yollar vardır IHttpClientFactory
:
En iyi yaklaşım, uygulamanın gereksinimlerine bağlıdır.
Temel kullanım
IHttpClientFactory
çağrılarak AddHttpClient
kaydedilebilir:
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.
bağımlılık IHttpClientFactory
ekleme (DI) kullanılarak istenebilir. Aşağıdaki kod örnek HttpClient
oluşturmak için kullanırIHttpClientFactory
:
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>();
}
}
}
IHttpClientFactory
Yukarıdaki örnekte like kullanmak, mevcut bir uygulamayı yeniden düzenlemenin iyi bir yoludur. Nasıl kullanıldığı üzerinde HttpClient
hiçbir etkisi yoktur. Mevcut bir uygulamada örneklerin oluşturulduğu yerlerde HttpClient
, bu oluşumları çağrısıyla CreateClientdeğiştirin.
Adlandırılmış istemciler
Adlandırılmış istemciler şu durumlarda iyi bir seçimdir:
- Uygulama için birçok farklı kullanım
HttpClient
gerekir. - Çoğu
HttpClient
farklı yapılandırmaya sahiptir.
Adlı bir ad HttpClient
için yapılandırma, kaydı Startup.ConfigureServices
sırasında belirtilebilir:
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");
});
İstemcinin yapılandırıldığı önceki kodda:
- temel adresi
https://api.github.com/
. - GitHub API'siyle çalışmak için iki üst bilgi gerekir.
CreateClient
Her seferinde CreateClient çağrılır:
- Yeni bir örneği
HttpClient
oluşturulur. - Yapılandırma eylemi çağrılır.
Adlandırılmış istemci oluşturmak için adını içine geçirin 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>();
}
}
}
Yukarıdaki kodda isteğin bir konak adı belirtmesi gerekmez. İstemci için yapılandırılan temel adres kullanıldığından kod yalnızca yolu geçirebilir.
Yazılan istemciler
Yazılan istemciler:
- Dizeleri anahtar olarak kullanmaya gerek kalmadan adlandırılmış istemcilerle aynı özellikleri sağlayın.
- İstemcileri kullanırken IntelliSense ve derleyici yardımı sağlar.
- Belirli
HttpClient
bir öğesini yapılandırmak ve ile etkileşime geçmek için tek bir konum sağlayın. Örneğin, tek bir türlenmiş istemci kullanılabilir:- Tek bir arka uç uç noktası için.
- Uç noktayla ilgili tüm mantığı kapsüllemek için.
- DI ile çalışın ve uygulamada gerektiğinde eklenebilir.
Yazılan istemci, oluşturucusunda bir HttpClient
parametreyi kabul eder:
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);
}
}
Kod açıklamalarının İngilizce dışındaki dillere çevirisini görmek isterseniz, bunu bu GitHub tartışma konusunda bize bildirin.
Önceki kodda:
- Yapılandırma türü belirtilen istemciye taşınır.
HttpClient
Nesnesi genel bir özellik olarak kullanıma sunulur.
İşlevselliği kullanıma sunan HttpClient
API'ye özgü yöntemler oluşturulabilir. Örneğin, GetAspNetDocsIssues
yöntemi açık sorunları almak için kodu kapsüller.
Aşağıdaki kod, türü belirtilen bir istemci sınıfını kaydetmek için öğesini Startup.ConfigureServices
çağırırAddHttpClient:
services.AddHttpClient<GitHubService>();
Yazılan istemci, DI ile geçici olarak kaydedilir. Önceki kodda geçici AddHttpClient
bir hizmet olarak kaydeder GitHubService
. Bu kayıt, aşağıdakiler için bir fabrika yöntemi kullanır:
HttpClient
örneği oluşturun.- örneğini
GitHubService
oluşturucusunda geçirerek bir örneğiHttpClient
oluşturun.
Yazılan istemci doğrudan eklenebilir ve kullanılabilir:
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>();
}
}
}
Türü belirtilmiş bir istemcinin yapılandırması, türü belirtilmiş istemcinin oluşturucusunda değil, içinde kayıt Startup.ConfigureServices
sırasında belirtilebilir:
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");
});
HttpClient
türü belirtilmiş bir istemci içinde kapsüllenebilir. Bunu bir özellik olarak ortaya çıkarmak yerine, örneği dahili olarak çağıran HttpClient
bir yöntem tanımlayın:
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);
}
}
Yukarıdaki kodda , HttpClient
özel bir alanda depolanır. erişimi HttpClient
genel GetRepos
yöntemiyle yapılır.
Oluşturulan istemciler
IHttpClientFactory
Yeniden Sığdır gibi üçüncü taraf kitaplıklarla birlikte kullanılabilir. Refit, .NET için bir REST kitaplıktır. API'leri REST canlı arabirimlere dönüştürür. Arabirimin bir uygulaması, dış HTTP çağrılarını yapmak için kullanılarak HttpClient
kullanılarak dinamik olarak RestService
oluşturulur.
Dış API'yi ve yanıtını temsil etmek için bir arabirim ve yanıt tanımlanır:
public interface IHelloClient
{
[Get("/helloworld")]
Task<Reply> GetMessageAsync();
}
public class Reply
{
public string Message { get; set; }
}
Uygulamayı oluşturmak için Refit kullanılarak türü oluşturulmuş bir istemci eklenebilir:
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();
}
Tanımlı arabirim, DI ve Refit tarafından sağlanan uygulamayla gerektiğinde kullanılabilir:
[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();
}
}
POST, PUT ve DELETE istekleri yapma
Yukarıdaki örneklerde, tüm HTTP istekleri GET HTTP fiilini kullanır. HttpClient
ayrıca aşağıdakiler dahil olmak üzere diğer HTTP fiillerini de destekler:
- POST
- PUT
- SİL
- PATCH
Desteklenen HTTP fiillerinin tam listesi için bkz HttpMethod. .
Aşağıdaki örnekte BIR HTTP POST isteğinin nasıl yapıldığını gösterilmektedir:
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();
}
Yukarıdaki kodda CreateItemAsync
yöntemi:
- parametresini
TodoItem
kullanarakSystem.Text.Json
JSON'a serileştirir. Bu, serileştirme işlemini yapılandırmak için örneğini JsonSerializerOptions kullanır. - HTTP isteğinin StringContent gövdesinde göndermek üzere serileştirilmiş JSON'ı paketlemek için bir örneği oluşturur.
- JSON içeriğini belirtilen URL'ye göndermek için çağrılar PostAsync . Bu, HttpClient.BaseAddress'e eklenen göreli bir URL'dir.
- Yanıt durum kodu başarıyı göstermiyorsa özel durum oluşturma çağrısı EnsureSuccessStatusCode yapar.
HttpClient
ayrıca diğer içerik türlerini de destekler. Örneğin, MultipartContent ve StreamContent. Desteklenen içeriğin tam listesi için bkz HttpContent. .
Aşağıdaki örnekte bir HTTP PUT isteği gösterilmektedir:
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();
}
Yukarıdaki kod POST örneğine çok benzer. SaveItemAsync
yöntemi yerine PostAsync
öğesini çağırırPutAsync.
Aşağıdaki örnekte bir HTTP DELETE isteği gösterilmektedir:
public async Task DeleteItemAsync(long itemId)
{
using var httpResponse =
await _httpClient.DeleteAsync($"/api/TodoItems/{itemId}");
httpResponse.EnsureSuccessStatusCode();
}
Önceki kodda DeleteItemAsync
yöntemi öğesini çağırır DeleteAsync. HTTP DELETE istekleri genellikle gövde içermediğinden, DeleteAsync
yöntemi bir örneğini HttpContent
kabul eden bir aşırı yükleme sağlamaz.
ile HttpClient
farklı HTTP fiilleri kullanma hakkında daha fazla bilgi edinmek için bkz HttpClient. .
Giden istek ara yazılımı
HttpClient
, giden HTTP istekleri için birbirine bağlanabilen işleyicileri temsilci olarak belirleme kavramına sahiptir. IHttpClientFactory
:
- Adlandırılmış her istemci için uygulanacak işleyicilerin tanımlanmasını basitleştirir.
- Giden istek ara yazılım işlem hattı oluşturmak için birden çok işleyicinin kaydını ve zincirini destekler. Bu işleyicilerin her biri giden istek öncesinde ve sonrasında iş gerçekleştirebilir. Bu desen:
- ASP.NET Core'daki gelen ara yazılım işlem hattına benzer.
- HTTP istekleriyle ilgili çapraz kesme sorunlarını yönetmek için aşağıdakiler gibi bir mekanizma sağlar:
- önbelleğe alma
- hata işleme
- Seri -leştirme
- günlüğe kaydetme
Temsilci işleyicisi oluşturmak için:
- 'den DelegatingHandlertüretilir.
- öğesini geçersiz kılın SendAsync. İsteği işlem hattındaki bir sonraki işleyiciye geçirmeden önce kodu yürütebilirsiniz:
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);
}
}
Yukarıdaki kod, üst bilginin istekte olup olmadığını X-API-KEY
denetler. Eksikse X-API-KEY
, BadRequest döndürülür.
ile Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandleriçin HttpClient
yapılandırmaya birden fazla işleyici eklenebilir:
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.
Yukarıdaki kodda ValidateHeaderHandler
, DI ile kaydedilir. Kaydedildikten sonra, AddHttpMessageHandler işleyici için türü geçirerek çağrılabilir.
Birden çok işleyici yürütülmeleri gereken sırayla kaydedilebilir. Her işleyici, son HttpClientHandler
isteği yürütene kadar sonraki işleyiciyi sarmalar:
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>();
Giden istek ara yazılımında DI kullanma
IHttpClientFactory
Yeni bir temsilci seçme işleyicisi oluşturduğunda, işleyicinin oluşturucu parametrelerini yerine getirmek için DI kullanır. IHttpClientFactory
her işleyici için ayrı bir DI kapsamı oluşturur ve bu da bir işleyici kapsamlı bir hizmet tükettiğinde şaşırtıcı davranışlara yol açabilir.
Örneğin, bir görevi tanımlayıcısı OperationId
olan bir işlem olarak temsil eden aşağıdaki arabirimi ve uygulamasını göz önünde bulundurun:
public interface IOperationScoped
{
string OperationId { get; }
}
public class OperationScoped : IOperationScoped
{
public string OperationId { get; } = Guid.NewGuid().ToString()[^4..];
}
Adından da anlaşılacağı gibi, IOperationScoped
kapsamlı bir yaşam süresi kullanılarak DI'ye kaydedilir:
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();
}
Aşağıdaki temsilci işleyicisi, giden isteğin X-OPERATION-ID
üst bilgisini ayarlamak için kullanır ve kullanırIOperationScoped
:
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);
}
}
İndirme] bölümünde HttpRequestsSample
sayfaya /Operation
gidin ve sayfayı yenileyin. İstek kapsamı değeri her istek için değişir, ancak işleyici kapsam değeri yalnızca 5 saniyede bir değişir.
İşleyiciler herhangi bir kapsamın hizmetlerine bağlı olabilir. İşleyicilerin bağımlı olduğu hizmetler, işleyici atıldığında atılır.
İstek başına durumu ileti işleyicileriyle paylaşmak için aşağıdaki yaklaşımlardan birini kullanın:
- kullanarak HttpRequestMessage.Propertiesişleyiciye veri geçirme.
- Geçerli isteğe erişmek için kullanın IHttpContextAccessor .
- Verileri geçirmek için özel AsyncLocal<T> bir depolama nesnesi oluşturun.
Polly tabanlı işleyicileri kullanma
IHttpClientFactory
Üçüncü taraf kitaplığı Polly ile tümleşir. Polly, .NET için kapsamlı bir dayanıklılık ve geçici hata işleme kitaplığıdır. Geliştiricilerin Yeniden Deneme, Devre Kesici, Zaman Aşımı, Bulkhead Yalıtımı ve Geri Dönüş gibi ilkeleri akıcı ve iş parçacığı açısından güvenli bir şekilde ifade etmesine olanak tanır.
Yapılandırılmış HttpClient
örneklerle Polly ilkelerinin kullanımını etkinleştirmek için uzantı yöntemleri sağlanır. Polly uzantıları, polly tabanlı işleyicilerin istemcilere eklenmesini destekler. Polly, Microsoft.Extensions.Http.Polly NuGet paketini gerektirir.
Geçici hataları işleme
Hatalar genellikle dış HTTP çağrıları geçici olduğunda oluşur. AddTransientHttpErrorPolicy geçici hataları işlemek için bir ilkenin tanımlanmasına izin verir. ile AddTransientHttpErrorPolicy
yapılandırılan ilkeler aşağıdaki yanıtları işler:
- HttpRequestException
- HTTP 5xx
- HTTP 408
AddTransientHttpErrorPolicy
olası bir PolicyBuilder
geçici hatayı temsil eden hataları işlemek için yapılandırılmış bir nesneye erişim sağlar:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<UnreliableEndpointCallerService>()
.AddTransientHttpErrorPolicy(p =>
p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));
// Remaining code deleted for brevity.
Önceki kodda bir WaitAndRetryAsync
ilke tanımlanmıştır. Başarısız istekler, denemeler arasında 600 ms gecikmeyle üç kez yeniden denendi.
İlkeleri dinamik olarak seçme
Polly tabanlı işleyiciler eklemek için uzantı yöntemleri sağlanır. Örneğin, AddPolicyHandler. Aşağıdaki AddPolicyHandler
aşırı yükleme, hangi ilkenin uygulanacağını belirlemek için isteği inceler:
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);
Yukarıdaki kodda, giden istek bir HTTP GET ise, 10 saniyelik bir zaman aşımı uygulanır. Diğer herhangi bir HTTP yöntemi için 30 saniyelik bir zaman aşımı kullanılır.
Birden çok Polly işleyicisi ekleme
Polly ilkelerini iç içe yerleştirme yaygın olarak görülür:
services.AddHttpClient("multiplepolicies")
.AddTransientHttpErrorPolicy(p => p.RetryAsync(3))
.AddTransientHttpErrorPolicy(
p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
Yukarıdaki örnekte:
- İki işleyici eklenir.
- İlk işleyici, yeniden deneme ilkesi eklemek için kullanır AddTransientHttpErrorPolicy . Başarısız istekler en fazla üç kez yeniden deneniyor.
- İkinci
AddTransientHttpErrorPolicy
çağrı bir devre kesici ilkesi ekler. 5 başarısız girişim sırayla gerçekleşirse 30 saniye boyunca daha fazla dış istek engellenir. Devre kesici ilkeleri durum bilgisi vardır. Bu istemci aracılığıyla yapılan tüm çağrılar aynı devre durumunu paylaşır.
Polly kayıt defterinden ilke ekleme
Düzenli olarak kullanılan ilkeleri yönetmeye yönelik bir yaklaşım, bunları bir kez tanımlamak ve ile PolicyRegistry
kaydetmektir.
Aşağıdaki kodda:
- "Normal" ve "uzun" ilkeler eklenir.
- AddPolicyHandlerFromRegistry kayıt defterinden "normal" ve "uzun" ilkeleri ekler.
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.
ve Polly tümleştirmeleri hakkında IHttpClientFactory
daha fazla bilgi için Polly wiki'sine bakın.
HttpClient ve yaşam süresi yönetimi
üzerinde IHttpClientFactory
her CreateClient
çağrıldığında yeni HttpClient
bir örnek döndürülür. HttpMessageHandler Adlandırılmış istemci başına bir oluşturulur. Fabrika, örneklerin yaşam ömrünü HttpMessageHandler
yönetir.
IHttpClientFactory
HttpMessageHandler
kaynak tüketimini azaltmak için fabrika tarafından oluşturulan örnekleri havuza alır. HttpMessageHandler
Yaşam süresi dolmadıysa, yeni HttpClient
bir örnek oluşturulurken bir örnek havuzdan yeniden kullanılabilir.
İşleyicilerin havuzu, her işleyici genellikle kendi temel HTTP bağlantılarını yönettiğinden tercih edilir. Gerekenden daha fazla işleyici oluşturmak bağlantı gecikmelerine neden olabilir. Bazı işleyiciler ayrıca bağlantıları süresiz olarak açık tutar ve bu da işleyicinin DNS (Etki Alanı Adı Sistemi) değişikliklerine tepki vermesini engelleyebilir.
Varsayılan işleyici ömrü iki dakikadır. Varsayılan değer, adlandırılmış istemci temelinde geçersiz kılınabilir:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("extendedhandlerlifetime")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
// Remaining code deleted for brevity.
HttpClient
örnekleri genellikle elden çıkarma gerektirmeyen .NET nesneleri olarak kabul edilebilir. Atma, giden istekleri iptal eder ve belirtilen HttpClient
örneğin çağrıldıktan Disposesonra kullanılamayabileceğini garanti eder. IHttpClientFactory
örnekler tarafından HttpClient
kullanılan kaynakları izler ve atar.
Tek HttpClient
bir örneği uzun süre canlı tutmak, başlangıcından IHttpClientFactory
önce kullanılan yaygın bir desendir. bu düzen' e IHttpClientFactory
geçirildikten sonra gereksiz hale gelir.
IHttpClientFactory için alternatifler
DI özellikli bir uygulamada kullanmak IHttpClientFactory
aşağıdakilerden kaçınıyor:
- Örnekleri havuza alarak
HttpMessageHandler
kaynak tükenme sorunları. - Örnekleri düzenli aralıklarla döngüye alarak
HttpMessageHandler
eski DNS sorunları.
Önceki sorunları çözmek için uzun ömürlü SocketsHttpHandler bir örnek kullanmanın alternatif yolları vardır.
- Uygulamanın ne zaman başladığının
SocketsHttpHandler
bir örneğini oluşturun ve uygulamanın ömrü boyunca kullanın. - DNS yenileme sürelerine göre uygun bir değere yapılandırın PooledConnectionLifetime .
- Gerektiğinde kullanarak
new HttpClient(handler, disposeHandler: false)
örnekler oluşturunHttpClient
.
Yukarıdaki yaklaşımlar, benzer şekilde çözülen IHttpClientFactory
kaynak yönetimi sorunlarını çözer.
- Örnekler
SocketsHttpHandler
arasındaHttpClient
bağlantıları paylaşır. Bu paylaşım yuva tükenmesini önler. - Eski
SocketsHttpHandler
DNS sorunlarını önlemek içinPooledConnectionLifetime
bağlantıları göre döngüye alır.
Tanımlama bilgileri
Havuza alınan HttpMessageHandler
örnekler, nesnelerin paylaşılmasıyla CookieContainer
sonuçlanır. Tahmin edilmeyen CookieContainer
nesne paylaşımı genellikle yanlış koda neden olur. Tanımlama bilgileri gerektiren uygulamalar için şunlardan birini göz önünde bulundurun:
- Otomatik cookie işlemeyi devre dışı bırakma
- Kaçın -arak
IHttpClientFactory
Otomatik cookie işlemeyi devre dışı bırakmak için çağrısıConfigurePrimaryHttpMessageHandler:
services.AddHttpClient("configured-disable-automatic-cookies")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
UseCookies = false,
};
});
Günlük Kaydı
Tüm istekler için kayıt günlüğü iletileri aracılığıyla IHttpClientFactory
oluşturulan istemciler. Varsayılan günlük iletilerini görmek için günlük yapılandırmasında uygun bilgi düzeyini etkinleştirin. İstek üst bilgilerinin günlüğe kaydedilmesi gibi ek günlükler yalnızca izleme düzeyinde eklenir.
Her istemci için kullanılan günlük kategorisi, istemcinin adını içerir. Örneğin MyNamedClient adlı bir istemci, "System.Net.Http.HttpClient kategorisine sahip iletileri günlüğe kaydeder.MyNamedClient. LogicalHandler". LogicalHandler ile son ekli iletiler, istek işleyicisi işlem hattının dışında gerçekleşir. İstekte, işlem hattındaki diğer işleyiciler işlemeden önce iletiler günlüğe kaydedilir. Yanıtta, diğer işlem hattı işleyicileri yanıtı aldıktan sonra iletiler günlüğe kaydedilir.
Günlük kaydı, istek işleyicisi işlem hattında da gerçekleşir. MyNamedClient örneğinde, bu iletiler "System.Net.Http.HttpClient" günlük kategorisiyle günlüğe kaydedilir.MyNamedClient. ClientHandler". İstek için bu, diğer tüm işleyiciler çalıştırıldıktan sonra ve istek gönderilmeden hemen önce gerçekleşir. Yanıtta, bu günlük işleyici işlem hattından geri geçmeden önce yanıtın durumunu içerir.
İşlem hattının dışında ve içinde günlüğe kaydetmeyi etkinleştirmek, diğer işlem hattı işleyicileri tarafından yapılan değişikliklerin denetlenebilmesini sağlar. Bu, istek üst bilgilerinde veya yanıt durum kodunda yapılan değişiklikleri içerebilir.
İstemcinin adını günlük kategorisine dahil etme, belirli adlandırılmış istemciler için günlük filtrelemeyi etkinleştirir.
HttpMessageHandler'ı yapılandırma
İstemci tarafından kullanılan iç HttpMessageHandler
öğesinin yapılandırmasını denetlemek gerekebilir.
IHttpClientBuilder
adlandırılmış veya yazılan istemciler eklenirken bir döndürülür. ConfigurePrimaryHttpMessageHandler Uzantı yöntemi bir temsilci tanımlamak için kullanılabilir. Temsilci, bu istemci tarafından kullanılan birincil HttpMessageHandler
değeri oluşturmak ve yapılandırmak için kullanılır:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("configured-inner-handler")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
AllowAutoRedirect = false,
UseDefaultCredentials = true
};
});
// Remaining code deleted for brevity.
Konsol uygulamasında IHttpClientFactory kullanma
Konsol uygulamasında projeye aşağıdaki paket başvurularını ekleyin:
Aşağıdaki örnekte:
- IHttpClientFactory, Genel Ana Bilgisayarın hizmet kapsayıcısında kayıtlıdır.
MyService
, hizmetten bir istemci fabrika örneği oluşturur ve bunu oluşturmakHttpClient
için kullanılır.HttpClient
bir web sayfasını almak için kullanılır.Main
hizmetinGetPage
yöntemini yürütmek için bir kapsam oluşturur ve web sayfası içeriğinin ilk 500 karakterini konsola yazar.
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}";
}
}
}
}
Üst bilgi yayma ara yazılımı
Üst bilgi yayma, http üst bilgilerini gelen istekten giden HTTP İstemcisi isteklerine yaymak için kullanılan bir ASP.NET Core ara yazılımıdır. Üst bilgi yayma özelliğini kullanmak için:
Ara yazılımı ve
HttpClient
içindeStartup
yapılandırın: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(); }); }
İstemci, giden isteklerde yapılandırılmış üst bilgileri içerir:
var client = clientFactory.CreateClient("MyForwardingClient"); var response = client.GetAsync(...);
Ek kaynaklar
Glenn Condron, Ryan Nowak ve Steve Gordon tarafından
, IHttpClientFactory bir uygulamada örnekleri yapılandırmak ve oluşturmak HttpClient için kaydedilebilir ve kullanılabilir. Aşağıdaki avantajları sunar:
- Mantıksal
HttpClient
örneklerini adlandırmak ve yapılandırmak için merkezi bir konum sağlar. Örneğin, github istemcisi kaydedilebilir ve GitHub'a erişecek şekilde yapılandırılabilir. Varsayılan istemci başka amaçlarla kaydedilebilir. - 'de
HttpClient
işleyicileri temsilci olarak atama yoluyla giden ara yazılım kavramını uyumlu hale getirir ve Polly tabanlı ara yazılıma bundan yararlanmak için uzantılar sağlar. - Yaşam süreleri el ile yönetildiğinde
HttpClient
oluşan yaygın DNS sorunlarını önlemek için temelHttpClientMessageHandler
alınan örneklerin havuzunu ve kullanım ömrünü yönetir. - Fabrika tarafından oluşturulan istemciler aracılığıyla gönderilen tüm istekler için yapılandırılabilir bir günlük deneyimi (aracılığıyla
ILogger
) ekler.
Örnek kodu görüntüleme veya indirme (indirme)
Önkoşullar
.NET Framework'ün hedeflenmesi için Microsoft.Extensions.Http NuGet paketinin yüklenmesi gerekir. .NET Core'ı hedefleyen ve Microsoft.AspNetCore.App meta paketine başvuran projeler paketi zaten içerirMicrosoft.Extensions.Http
.
Tüketim desenleri
Bir uygulamada kullanılabilecek çeşitli yollar vardır IHttpClientFactory
:
Hiçbiri kesinlikle başkalarından üstün değil. En iyi yaklaşım, uygulamanın kısıtlamalarına bağlıdır.
Temel kullanım
IHttpClientFactory
, yönteminin AddHttpClient
içinde Startup.ConfigureServices
uzantısı IServiceCollection
yöntemi çağrılarak kaydedilebilir.
services.AddHttpClient();
Kaydedildikten sonra kod, bağımlılık ekleme (DI) ile her yerden hizmet eklenmesini kabul IHttpClientFactory
edebilir. IHttpClientFactory
örneği HttpClient
oluşturmak için kullanılabilir:
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>();
}
}
}
Bu şekilde kullanmak IHttpClientFactory
, mevcut bir uygulamayı yeniden düzenlemenin iyi bir yoludur. Kullanılma şekli HttpClient
üzerinde hiçbir etkisi yoktur. Örneklerin HttpClient
şu anda oluşturulduğu yerlerde, bu oluşumları çağrısıyla CreateClientdeğiştirin.
Adlandırılmış istemciler
Bir uygulama, her biri HttpClient
farklı bir yapılandırmaya sahip birçok farklı kullanımı gerektiriyorsa, adlandırılmış istemcileri kullanma seçeneği vardır. Adlı bir ad HttpClient
için yapılandırma, kaydı Startup.ConfigureServices
sırasında belirtilebilir.
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");
});
Yukarıdaki kodda, AddHttpClient
github adını sağlayarak çağrılır. Bu istemcide bazı varsayılan yapılandırmalar uygulanmıştır( temel adres ve GitHub API'siyle çalışmak için gereken iki üst bilgi).
Her çağrılır CreateClient
, yeni bir örneği HttpClient
oluşturulur ve yapılandırma eylemi çağrılır.
Adlandırılmış bir istemciyi kullanmak için, öğesine CreateClient
bir dize parametresi geçirilebilir. Oluşturulacak istemcinin adını belirtin:
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>();
}
}
}
Yukarıdaki kodda isteğin bir konak adı belirtmesi gerekmez. İstemci için yapılandırılan temel adres kullanıldığından yalnızca yolu geçirebilir.
Yazılan istemciler
Yazılan istemciler:
- Dizeleri anahtar olarak kullanmaya gerek kalmadan adlandırılmış istemcilerle aynı özellikleri sağlayın.
- İstemcileri kullanırken IntelliSense ve derleyici yardımı sağlar.
- Belirli
HttpClient
bir öğesini yapılandırmak ve ile etkileşime geçmek için tek bir konum sağlayın. Örneğin, tek bir arka uç uç noktası için tek bir türü belirtilmiş istemci kullanılabilir ve bu uç noktayla ilgili tüm mantığı kapsülleyebilir. - DI ile çalışın ve uygulamanızda gerektiğinde eklenebilir.
Yazılan istemci, oluşturucusunda bir HttpClient
parametreyi kabul eder:
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;
}
}
Yukarıdaki kodda, yapılandırma türü belirtilen istemciye taşınır. HttpClient
Nesnesi genel bir özellik olarak kullanıma sunulur. İşlevselliği kullanıma sunan HttpClient
API'ye özgü yöntemler tanımlamak mümkündür. yöntemi, GetAspNetDocsIssues
GitHub deposundaki en son açık sorunları sorgulamak ve ayrıştırmak için gereken kodu kapsüller.
Yazılan bir istemciyi kaydetmek için, genel AddHttpClient uzantı yöntemi, türü belirtilen istemci sınıfını belirterek içinde Startup.ConfigureServices
kullanılabilir:
services.AddHttpClient<GitHubService>();
Yazılan istemci, DI ile geçici olarak kaydedilir. Yazılan istemci doğrudan eklenebilir ve kullanılabilir:
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>();
}
}
}
Tercih edilirse, türü belirtilmiş bir istemcinin yapılandırması, türü belirtilmiş istemcinin oluşturucusunda değil, içinde Startup.ConfigureServices
kayıt sırasında belirtilebilir:
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");
});
türü belirtilmiş bir istemci içinde öğesini tamamen kapsüllemek HttpClient
mümkündür. Bunu bir özellik olarak ortaya çıkarmak yerine örneği dahili olarak çağıran HttpClient
genel yöntemler sağlanabilir.
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;
}
}
Yukarıdaki kodda HttpClient
, özel bir alan olarak depolanır. Dış çağrılar yapmak için tüm erişim yöntemi üzerinden GetRepos
gider.
Oluşturulan istemciler
IHttpClientFactory
Yeniden Sığdır gibi diğer üçüncü taraf kitaplıklarla birlikte kullanılabilir. Refit, .NET için bir REST kitaplıktır. API'leri REST canlı arabirimlere dönüştürür. Arabirimin bir uygulaması, dış HTTP çağrılarını yapmak için kullanılarak HttpClient
kullanılarak dinamik olarak RestService
oluşturulur.
Dış API'yi ve yanıtını temsil etmek için bir arabirim ve yanıt tanımlanır:
public interface IHelloClient
{
[Get("/helloworld")]
Task<Reply> GetMessageAsync();
}
public class Reply
{
public string Message { get; set; }
}
Uygulamayı oluşturmak için Refit kullanılarak türü oluşturulmuş bir istemci eklenebilir:
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();
}
Tanımlı arabirim, DI ve Refit tarafından sağlanan uygulamayla gerektiğinde kullanılabilir:
[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();
}
}
Giden istek ara yazılımı
HttpClient
zaten giden HTTP istekleri için birbirine bağlanabilen işleyicileri temsilci olarak belirleme kavramına sahiptir. , IHttpClientFactory
her adlandırılmış istemci için uygulanacak işleyicileri tanımlamayı kolaylaştırır. Giden bir istek ara yazılım işlem hattı oluşturmak için birden çok işleyicinin kaydını ve zincirini destekler. Bu işleyicilerin her biri giden istek öncesinde ve sonrasında iş gerçekleştirebilir. Bu desen, ASP.NET Core'daki gelen ara yazılım işlem hattına benzer. Desen, http istekleriyle ilgili önbelleğe alma, hata işleme, serileştirme ve günlüğe kaydetme gibi çapraz kesme sorunlarını yönetmeye yönelik bir mekanizma sağlar.
İşleyici oluşturmak için öğesinden DelegatingHandlertüretilen bir sınıf tanımlayın. İsteği işlem hattındaki SendAsync
bir sonraki işleyiciye geçirmeden önce kodu yürütmek için yöntemini geçersiz kılın:
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);
}
}
Yukarıdaki kod temel bir işleyici tanımlar. İsteğe bir X-API-KEY
üst bilgi eklenip eklenmediğini denetler. Üst bilgi eksikse, HTTP çağrısından kaçınabilir ve uygun bir yanıt döndürebilir.
Kayıt sırasında, yapılandırmasına bir HttpClient
veya daha fazla işleyici eklenebilir. Bu görev üzerindeki IHttpClientBuilderuzantı yöntemleriyle gerçekleştirilir.
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>();
Yukarıdaki kodda ValidateHeaderHandler
, DI ile kaydedilir. İşleyicinin , hiçbir zaman kapsamı belirlenmiş, geçici bir hizmet olarak DI'ye kaydedilmesi gerekir . İşleyici kapsamlı bir hizmet olarak kayıtlıysa ve işleyicinin bağımlı olduğu hizmetler atılabilirse:
- İşleyicinin hizmetleri, işleyici kapsamın dışına çıkmadan önce atılabilir.
- Atılan işleyici hizmetleri işleyicinin başarısız olmasına neden olur.
Kaydedildikten sonra, AddHttpMessageHandler işleyici türü geçirerek çağrılabilir.
Birden çok işleyici yürütülmeleri gereken sırayla kaydedilebilir. Her işleyici, son HttpClientHandler
isteği yürütene kadar sonraki işleyiciyi sarmalar:
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>();
İstek başına durumu ileti işleyicileriyle paylaşmak için aşağıdaki yaklaşımlardan birini kullanın:
- kullanarak
HttpRequestMessage.Properties
işleyiciye veri geçirme. - Geçerli isteğe erişmek için kullanın
IHttpContextAccessor
. - Verileri geçirmek için özel
AsyncLocal
bir depolama nesnesi oluşturun.
Polly tabanlı işleyicileri kullanma
IHttpClientFactory
Polly adlı popüler bir üçüncü taraf kitaplığıyla tümleşir. Polly, .NET için kapsamlı bir dayanıklılık ve geçici hata işleme kitaplığıdır. Geliştiricilerin Yeniden Deneme, Devre Kesici, Zaman Aşımı, Bulkhead Yalıtımı ve Geri Dönüş gibi ilkeleri akıcı ve iş parçacığı açısından güvenli bir şekilde ifade etmesine olanak tanır.
Yapılandırılmış HttpClient
örneklerle Polly ilkelerinin kullanımını etkinleştirmek için uzantı yöntemleri sağlanır. Polly uzantıları:
- İstemcilere Polly tabanlı işleyiciler eklemeyi destekler.
- Microsoft.Extensions.Http.Polly NuGet paketi yüklendikten sonra kullanılabilir. Paket, ASP.NET Core paylaşılan çerçevesine dahil değildir.
Geçici hataları işleme
En yaygın hatalar dış HTTP çağrıları geçici olduğunda oluşur. Adlı AddTransientHttpErrorPolicy
kullanışlı bir uzantı yöntemi, geçici hataları işlemek için bir ilkenin tanımlanmasını sağlayan eklenmiştir. Bu uzantı yöntemiyle yapılandırılan ilkeler , HTTP 5xx yanıtları ve HTTP 408 yanıtlarını işler HttpRequestException
.
Uzantı içinde AddTransientHttpErrorPolicy
Startup.ConfigureServices
kullanılabilir. Uzantı, olası bir PolicyBuilder
geçici hatayı temsil eden hataları işlemek için yapılandırılmış bir nesneye erişim sağlar:
services.AddHttpClient<UnreliableEndpointCallerService>()
.AddTransientHttpErrorPolicy(p =>
p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));
Önceki kodda bir WaitAndRetryAsync
ilke tanımlanmıştır. Başarısız istekler, denemeler arasında 600 ms gecikmeyle üç kez yeniden denendi.
İlkeleri dinamik olarak seçme
Polly tabanlı işleyiciler eklemek için kullanılabilecek ek uzantı yöntemleri vardır. Bu tür uzantılardan biri, birden çok aşırı yüklemesi olan uzantısıdır AddPolicyHandler
. Bir aşırı yükleme, hangi ilkenin uygulanacağını tanımlarken isteğin incelenmesini sağlar:
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);
Yukarıdaki kodda, giden istek bir HTTP GET ise, 10 saniyelik bir zaman aşımı uygulanır. Diğer herhangi bir HTTP yöntemi için 30 saniyelik bir zaman aşımı kullanılır.
Birden çok Polly işleyicisi ekleme
Gelişmiş işlevsellik sağlamak için Polly ilkelerini iç içe yerleştirme yaygın olarak görülür:
services.AddHttpClient("multiplepolicies")
.AddTransientHttpErrorPolicy(p => p.RetryAsync(3))
.AddTransientHttpErrorPolicy(
p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
Yukarıdaki örnekte iki işleyici eklenmiştir. İlki, yeniden deneme ilkesi eklemek için uzantıyı AddTransientHttpErrorPolicy
kullanır. Başarısız istekler en fazla üç kez yeniden deneniyor. İkinci çağrısı bir AddTransientHttpErrorPolicy
devre kesici ilkesi ekler. Beş başarısız deneme sırayla gerçekleşirse 30 saniye boyunca daha fazla dış istek engellenir. Devre kesici ilkeleri durum bilgisi vardır. Bu istemci aracılığıyla yapılan tüm çağrılar aynı devre durumunu paylaşır.
Polly kayıt defterinden ilke ekleme
Düzenli olarak kullanılan ilkeleri yönetmeye yönelik bir yaklaşım, bunları bir kez tanımlamak ve ile PolicyRegistry
kaydetmektir. İşleyicinin kayıt defterinden bir ilke kullanılarak eklenmesini sağlayan bir uzantı yöntemi sağlanır:
var registry = services.AddPolicyRegistry();
registry.Add("regular", timeout);
registry.Add("long", longTimeout);
services.AddHttpClient("regulartimeouthandler")
.AddPolicyHandlerFromRegistry("regular");
Yukarıdaki kodda, öğesine eklendiğinde PolicyRegistry
ServiceCollection
iki ilke kaydedilir. Kayıt defterinden bir ilke kullanmak için yöntemi AddPolicyHandlerFromRegistry
kullanılır ve uygulanacak ilkenin adı geçirilir.
ve Polly tümleştirmeleri hakkında IHttpClientFactory
daha fazla bilgiyi Polly wiki'sinde bulabilirsiniz.
HttpClient ve yaşam süresi yönetimi
üzerinde IHttpClientFactory
her CreateClient
çağrıldığında yeni HttpClient
bir örnek döndürülür. Adlandırılmış istemci başına bir HttpMessageHandler vardır. Fabrika, örneklerin yaşam ömrünü HttpMessageHandler
yönetir.
IHttpClientFactory
HttpMessageHandler
kaynak tüketimini azaltmak için fabrika tarafından oluşturulan örnekleri havuza alır. HttpMessageHandler
Yaşam süresi dolmadıysa, yeni HttpClient
bir örnek oluşturulurken bir örnek havuzdan yeniden kullanılabilir.
İşleyicilerin havuzu, her işleyici genellikle kendi temel HTTP bağlantılarını yönettiğinden tercih edilir. Gerekenden daha fazla işleyici oluşturmak bağlantı gecikmelerine neden olabilir. Bazı işleyiciler ayrıca bağlantıları süresiz olarak açık tutar ve bu da işleyicinin DNS değişikliklerine tepki vermesini engelleyebilir.
Varsayılan işleyici ömrü iki dakikadır. Varsayılan değer, adlandırılmış istemci temelinde geçersiz kılınabilir. Geçersiz kılmak için, istemciyi oluştururken döndürülen öğesini çağırın SetHandlerLifetime IHttpClientBuilder
:
services.AddHttpClient("extendedhandlerlifetime")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
İstemcinin elden çıkarılması gerekli değildir. Atma, giden istekleri iptal eder ve belirtilen HttpClient
örneğin çağrıldıktan Disposesonra kullanılamayabileceğini garanti eder. IHttpClientFactory
örnekler tarafından HttpClient
kullanılan kaynakları izler ve atar. Örnekler HttpClient
genellikle elden çıkarma gerektirmeyen .NET nesneleri olarak kabul edilebilir.
Tek HttpClient
bir örneği uzun süre canlı tutmak, başlangıcından IHttpClientFactory
önce kullanılan yaygın bir desendir. bu düzen' e IHttpClientFactory
geçirildikten sonra gereksiz hale gelir.
IHttpClientFactory için alternatifler
DI özellikli bir uygulamada kullanmak IHttpClientFactory
aşağıdakilerden kaçınıyor:
- Örnekleri havuza alarak
HttpMessageHandler
kaynak tükenme sorunları. - Örnekleri düzenli aralıklarla döngüye alarak
HttpMessageHandler
eski DNS sorunları.
Önceki sorunları çözmek için uzun ömürlü SocketsHttpHandler bir örnek kullanmanın alternatif yolları vardır.
- Uygulamanın ne zaman başladığının
SocketsHttpHandler
bir örneğini oluşturun ve uygulamanın ömrü boyunca kullanın. - DNS yenileme sürelerine göre uygun bir değere yapılandırın PooledConnectionLifetime .
- Gerektiğinde kullanarak
new HttpClient(handler, disposeHandler: false)
örnekler oluşturunHttpClient
.
Yukarıdaki yaklaşımlar, benzer şekilde çözülen IHttpClientFactory
kaynak yönetimi sorunlarını çözer.
- Örnekler
SocketsHttpHandler
arasındaHttpClient
bağlantıları paylaşır. Bu paylaşım yuva tükenmesini önler. - Eski
SocketsHttpHandler
DNS sorunlarını önlemek içinPooledConnectionLifetime
bağlantıları göre döngüye alır.
Tanımlama bilgileri
Havuza alınan HttpMessageHandler
örnekler, nesnelerin paylaşılmasıyla CookieContainer
sonuçlanır. Tahmin edilmeyen CookieContainer
nesne paylaşımı genellikle yanlış koda neden olur. Tanımlama bilgileri gerektiren uygulamalar için şunlardan birini göz önünde bulundurun:
- Otomatik cookie işlemeyi devre dışı bırakma
- Kaçın -arak
IHttpClientFactory
Otomatik cookie işlemeyi devre dışı bırakmak için çağrısıConfigurePrimaryHttpMessageHandler:
services.AddHttpClient("configured-disable-automatic-cookies")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
UseCookies = false,
};
});
Günlük Kaydı
Tüm istekler için kayıt günlüğü iletileri aracılığıyla IHttpClientFactory
oluşturulan istemciler. Varsayılan günlük iletilerini görmek için günlük yapılandırmanızda uygun bilgi düzeyini etkinleştirin. İstek üst bilgilerinin günlüğe kaydedilmesi gibi ek günlükler yalnızca izleme düzeyinde eklenir.
Her istemci için kullanılan günlük kategorisi, istemcinin adını içerir. Örneğin MyNamedClient adlı bir istemci, kategorisine System.Net.Http.HttpClient.MyNamedClient.LogicalHandler
sahip iletileri günlüğe kaydeder. LogicalHandler ile son ekli iletiler, istek işleyicisi işlem hattının dışında gerçekleşir. İstekte, işlem hattındaki diğer işleyiciler işlemeden önce iletiler günlüğe kaydedilir. Yanıtta, diğer işlem hattı işleyicileri yanıtı aldıktan sonra iletiler günlüğe kaydedilir.
Günlük kaydı, istek işleyicisi işlem hattında da gerçekleşir. MyNamedClient örneğinde, bu iletiler günlük kategorisine System.Net.Http.HttpClient.MyNamedClient.ClientHandler
göre günlüğe kaydedilir. İstek için bu, diğer tüm işleyiciler çalıştırıldıktan sonra ve istek ağ üzerinden gönderilmeden hemen önce gerçekleşir. Yanıtta, bu günlük işleyici işlem hattından geri geçmeden önce yanıtın durumunu içerir.
İşlem hattının dışında ve içinde günlüğe kaydetmeyi etkinleştirmek, diğer işlem hattı işleyicileri tarafından yapılan değişikliklerin denetlenebilmesini sağlar. Bu, örneğin istek üst bilgilerinde veya yanıt durum kodunda yapılan değişiklikleri içerebilir.
İstemcinin adını günlük kategorisine dahil etme, gerektiğinde belirli adlandırılmış istemciler için günlük filtrelemeyi etkinleştirir.
HttpMessageHandler'ı yapılandırma
İstemci tarafından kullanılan iç HttpMessageHandler
öğesinin yapılandırmasını denetlemek gerekebilir.
IHttpClientBuilder
adlandırılmış veya yazılan istemciler eklenirken bir döndürülür. ConfigurePrimaryHttpMessageHandler Uzantı yöntemi bir temsilci tanımlamak için kullanılabilir. Temsilci, bu istemci tarafından kullanılan birincil HttpMessageHandler
değeri oluşturmak ve yapılandırmak için kullanılır:
services.AddHttpClient("configured-inner-handler")
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
AllowAutoRedirect = false,
UseDefaultCredentials = true
};
});
Konsol uygulamasında IHttpClientFactory kullanma
Konsol uygulamasında projeye aşağıdaki paket başvurularını ekleyin:
Aşağıdaki örnekte:
- IHttpClientFactory, Genel Ana Bilgisayarın hizmet kapsayıcısında kayıtlıdır.
MyService
, hizmetten bir istemci fabrika örneği oluşturur ve bunu oluşturmakHttpClient
için kullanılır.HttpClient
bir web sayfasını almak için kullanılır.GetPage
Hizmetin yöntemi, web sayfası içeriğinin ilk 500 karakterini konsola yazmak için yürütülür. 'denProgram.Main
hizmetleri çağırma hakkında daha fazla bilgi için bkz . ASP.NET Core'da bağımlılık ekleme.
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}";
}
}
}
}
Üst bilgi yayma ara yazılımı
Üst bilgi yayma, gelen istekten giden HTTP İstemcisi isteklerine HTTP üst bilgilerini yaymak için topluluk tarafından desteklenen bir ara yazılımdır. Üst bilgi yayma özelliğini kullanmak için:
HeaderPropagation paketinin topluluk tarafından desteklenen bağlantı noktasına başvurun. ASP.NET Core 3.1 ve üzeri Microsoft.AspNetCore.HeaderPropagation'ı destekler.
Ara yazılımı ve
HttpClient
içindeStartup
yapılandırın: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(); }
İstemci, giden isteklerde yapılandırılmış üst bilgileri içerir:
var client = clientFactory.CreateClient("MyForwardingClient"); var response = client.GetAsync(...);
Ek kaynaklar
ASP.NET Core