ASP.NET Core'da IHttpClientFactory kullanarak HTTP isteklerinde bulunma

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ılar HttpClientsağlar.
  • Temel HttpClientMessageHandler alınan örneklerin havuzunu ve ömrünü yönetir. Otomatik yönetim, yaşam süreleri el ile yönetildiğinde HttpClient 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 ON içeriğini seri durumdan çıkarmak JSiçin kullanırSystem.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.csarayarak 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 HttpClientgerekir.
  • Çoğu HttpClientfarklı yapılandırmaya sahiptir.

içinde kaydı Program.cssı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 HttpClientbir öğ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:

  1. HttpClient örneği oluşturun.
  2. örneğini GitHubServiceoluşturucusunda geçirerek bir örneği HttpClient 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.cssırasında da belirtilebilir:

builder.Services.AddHttpClient<GitHubService>(httpClient =>
{
    httpClient.BaseAddress = new Uri("https://api.github.com/");

    // ...
});

Oluşturulan istemciler

IHttpClientFactoryYeniden 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 IGitHubClienteriş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 kullanarak System.Text.JsonON olarak JSserileştirir.
  • HTTP isteğinin StringContent gövdesinde göndermek üzere serileştirilmiş JSON paketinin bir örneğini oluşturur.
  • BELIRTILEN URL'ye JSON içeriğini göndermek için çağrılarPostAsync. 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 HttpContentkabul eden bir aşırı yükleme sağlamaz.

ile HttpClientfarklı 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. IHttpClientFactoryher 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ı OperationIdolan 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 HttpRequestsSamplesayfası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:

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:

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 PolicyRegistrykaydetmektir. Örnek:

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 ve Longeklenir.
  • 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 IHttpClientFactoryher 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.

IHttpClientFactoryHttpMessageHandler 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 IHttpClientFactorygeç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ında HttpClient bağlantıları paylaşır. Bu paylaşım yuva tükenmesini önler.
  • Eski SocketsHttpHandler DNS sorunlarını önlemek için PooledConnectionLifetime 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
        });

CookieS

Havuza alınan HttpMessageHandler örnekler, nesnelerin paylaşılmasıyla CookieContainer sonuçlanır. Tahmin edilmeyen CookieContainer nesne paylaşımı genellikle yanlış koda neden olur. s gerektiren cookieuygulamalar 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ğini IHttpClientFactorytalep eder.
  • GitHubService, belgeleri GitHub dallarını almak için kullandığı bir örneğini HttpClientoluş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çinde Program.csyapı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ılar HttpClientsağlar.
  • Temel HttpClientMessageHandler alınan örneklerin havuzunu ve ömrünü yönetir. Otomatik yönetim, yaşam süreleri el ile yönetildiğinde HttpClient 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 ON içeriğini seri durumdan çıkarmak JSiçin kullanırSystem.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 AddHttpClientkaydedilebilir:

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 HttpClientgerekir.
  • Çoğu HttpClientfarklı yapılandırmaya sahiptir.

Adlı bir ad HttpClient için yapılandırma, kaydı Startup.ConfigureServicessı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 HttpClientbir öğ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:

  1. HttpClient örneği oluşturun.
  2. örneğini GitHubServiceoluşturucusunda geçirerek bir örneği HttpClient 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.ConfigureServicessı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

IHttpClientFactoryYeniden 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 RestServiceoluş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 kullanarak System.Text.JsonON olarak JSserileş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 paketinin bir örneğini oluşturur.
  • BELIRTILEN URL'ye JSON içeriğini göndermek için çağrılarPostAsync. 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 HttpContentkabul eden bir aşırı yükleme sağlamaz.

ile HttpClientfarklı 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. IHttpClientFactoryher 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ı OperationIdolan 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:

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:

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 PolicyRegistrykaydetmektir.

Aşağıdaki kodda:

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 IHttpClientFactoryher 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.

IHttpClientFactoryHttpMessageHandler 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 IHttpClientFactorygeç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ında HttpClient bağlantıları paylaşır. Bu paylaşım yuva tükenmesini önler.
  • Eski SocketsHttpHandler DNS sorunlarını önlemek için PooledConnectionLifetime bağlantıları göre döngüye alır.

CookieS

Havuza alınan HttpMessageHandler örnekler, nesnelerin paylaşılmasıyla CookieContainer sonuçlanır. Tahmin edilmeyen CookieContainer nesne paylaşımı genellikle yanlış koda neden olur. s gerektiren cookieuygulamalar 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şturmak HttpClientiçin kullanılır. HttpClient bir web sayfasını almak için kullanılır.
  • Main hizmetin GetPage 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:

  • Microsoft.AspNetCore.HeaderPropagation paketine başvurun.

  • Ara yazılımı ve HttpClient içinde Startupyapı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ılar HttpClientsağlar.
  • Temel HttpClientMessageHandler alınan örneklerin havuzunu ve ömrünü yönetir. Otomatik yönetim, yaşam süreleri el ile yönetildiğinde HttpClient 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 ON içeriğini seri durumdan çıkarmak JSiçin kullanırSystem.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 AddHttpClientkaydedilebilir:

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 HttpClientgerekir.
  • Çoğu HttpClientfarklı yapılandırmaya sahiptir.

Adlı bir ad HttpClient için yapılandırma, kaydı Startup.ConfigureServicessı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 HttpClientbir öğ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:

  1. HttpClient örneği oluşturun.
  2. örneğini GitHubServiceoluşturucusunda geçirerek bir örneği HttpClient 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.ConfigureServicessı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

IHttpClientFactoryYeniden 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 RestServiceoluş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 kullanarak System.Text.JsonON olarak JSserileş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 paketinin bir örneğini oluşturur.
  • BELIRTILEN URL'ye JSON içeriğini göndermek için çağrılarPostAsync. 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 HttpContentkabul eden bir aşırı yükleme sağlamaz.

ile HttpClientfarklı 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. IHttpClientFactoryher 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ı OperationIdolan 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:

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:

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 PolicyRegistrykaydetmektir.

Aşağıdaki kodda:

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 IHttpClientFactoryher 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.

IHttpClientFactoryHttpMessageHandler 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 IHttpClientFactorygeç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ında HttpClient bağlantıları paylaşır. Bu paylaşım yuva tükenmesini önler.
  • Eski SocketsHttpHandler DNS sorunlarını önlemek için PooledConnectionLifetime bağlantıları göre döngüye alır.

CookieS

Havuza alınan HttpMessageHandler örnekler, nesnelerin paylaşılmasıyla CookieContainer sonuçlanır. Tahmin edilmeyen CookieContainer nesne paylaşımı genellikle yanlış koda neden olur. s gerektiren cookieuygulamalar 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şturmak HttpClientiçin kullanılır. HttpClient bir web sayfasını almak için kullanılır.
  • Main hizmetin GetPage 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:

  • Microsoft.AspNetCore.HeaderPropagation paketine başvurun.

  • Ara yazılımı ve HttpClient içinde Startupyapı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 temel HttpClientMessageHandler 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)

Ön koş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ı IServiceCollectionyö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 HttpClientfarklı 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.ConfigureServicessı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 CreateClientbir 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 HttpClientbir öğ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.ConfigureServiceskullanı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.ConfigureServiceskayı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

IHttpClientFactoryYeniden 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 RestServiceoluş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 HttpClientveya 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.Propertiesiş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

IHttpClientFactoryPolly 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 AddTransientHttpErrorPolicyStartup.ConfigureServiceskullanı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 PolicyRegistrykaydetmektir. İş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 PolicyRegistryServiceCollectioniki 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 IHttpClientFactoryher 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.

IHttpClientFactoryHttpMessageHandler 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 SetHandlerLifetimeIHttpClientBuilder :

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 IHttpClientFactorygeç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ında HttpClient bağlantıları paylaşır. Bu paylaşım yuva tükenmesini önler.
  • Eski SocketsHttpHandler DNS sorunlarını önlemek için PooledConnectionLifetime bağlantıları göre döngüye alır.

CookieS

Havuza alınan HttpMessageHandler örnekler, nesnelerin paylaşılmasıyla CookieContainer sonuçlanır. Tahmin edilmeyen CookieContainer nesne paylaşımı genellikle yanlış koda neden olur. s gerektiren cookieuygulamalar 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.LogicalHandlersahip 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.ClientHandlergö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şturmak HttpClientiç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. 'den Program.Mainhizmetleri ç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çinde Startupyapı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