ASP.NET Core'da IHttpClientFactory kullanarak HTTP istekleri yapma

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 örnekleri 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 HttpClienthale getirir. içinde işleyicileri temsilci 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ürelerini el ile yönetirken 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 başlığı sürümündeki örnek kod, HTTP yanıtlarında döndürülen ON içeriğini seri durumdan çıkarma JSamacıyla kullanırSystem.Text.Json. ve ReadAsAsync<T>kullanan Json.NET örnekler için bu konunun 2.x sürümünü seçmek için sürüm seçiciyi kullanın.

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();

IHttpClientFactorybağımlılık ekleme (DI) kullanılarak istenebilir. Aşağıdaki kod bir HttpClient örnek 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 Önceki örnekte like kullanmak, mevcut bir uygulamayı yeniden düzenlemenin iyi bir yoludur. Bunun nasıl HttpClient kullanıldığı üzerinde 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.
  • Birçoğu HttpClientfarklı yapılandırmaya sahiptir.

adlı bir adın HttpClient içinde Program.cskaydı sırasında yapılandırmasını 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ığı yukarıdaki 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 HttpClient örneği oluşturulur.
  • Yapılandırma eylemi çağrılır.

Adlandırılmış istemci oluşturmak için adını içine CreateClientgeçirin:

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 ana bilgisayar 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.
  • Yapılandırmak ve belirli HttpClientbir ile etkileşime geçmek için tek bir konum sağlayın. Örneğin, tek bir türü yazılan 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.

Türü yazılan istemci, oluşturucusunda bir HttpClient parametre 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");
}

Yukarıdaki 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 HttpClient sunan API'ye özgü yöntemler oluşturulabilir. Örneğin yöntemi, GetAspNetCoreDocsBranches GitHub dallarını almak için kodu kapsüller.

Aşağıdaki kodAddHttpClient, türü belirtilen istemci sınıfını GitHubService kaydetmek için çağrısı Program.cs yapar:

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şturucusunun bir örneğine geçirerek öğesinin HttpClient bir örneğini 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

IHttpClientFactoryRefit 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 de dahil olmak üzere diğer HTTP fiillerini destekler:

  • POST
  • PUT
  • DELETE
  • YAMA

Desteklenen HTTP fiillerinin tam listesi için bkz HttpMethod. .

Aşağıdaki örnekte HTTP POST isteği oluşturma adımları 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:

HttpClient 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();
}

Yukarıdaki kodda yöntemi çağrısında bulunur DeleteItemAsyncDeleteAsync. 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ğlanabilecek işleyicileri temsilci olarak belirleme kavramına sahiptir. IHttpClientFactory:

  • Her adlandırılmış 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 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 belirleme 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 döndürülür BadRequest .

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 istek yürütene kadar sonraki işleyiciyi sarmalar:

builder.Services.AddTransient<SampleHandler1>();
builder.Services.AddTransient<SampleHandler2>();

builder.Services.AddHttpClient("MultipleHttpMessageHandlers")
    .AddHttpMessageHandler<SampleHandler1>()
    .AddHttpMessageHandler<SampleHandler2>();

Önceki kodda, SampleHandler1 önce, önce SampleHandler2öğesini çalıştırır.

Giden istek ara yazılımında DI kullanma

IHttpClientFactory Yeni bir temsilci seçme işleyicisi oluşturduğunda, işleyicinin oluşturucu parametrelerini yerine getirmek için DI kullanır. IHttpClientFactory her işleyici için ayrı bir DI kapsamı oluşturur ve bu da işleyici kapsamı belirlenmiş bir hizmeti 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, IOperationScopedkapsamlı bir yaşam süresi kullanılarak DI'ye kaydedilir:

builder.Services.AddScoped<IOperationScoped, OperationScoped>();

Aşağıdaki temsilci belirleme 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);
    }
}

İndirmedeHttpRequestsSample sayfasına gidin /Operation ve sayfayı yenileyin. İstek kapsamı değeri her istek için değişir, ancak işleyici kapsam değeri yalnızca 5 saniyede bir değişir.

İşleyiciler herhangi bir kapsamdaki hizmetlere bağımlı 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ı, istemcilere Polly tabanlı işleyiciler eklemeyi destekler. Polly için Microsoft.Extensions.Http.Polly NuGet paketi gerekir.

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ını sağlar. 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 deneniyor.

İ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 tüm HTTP yöntemleri için 30 saniyelik 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 deneme sırayla gerçekleşirse 30 saniye boyunca başka dış istekler engellenir. Devre kesici ilkeleri durum bilgisi olan ilkelerdir. Bu istemci aracılığıyla yapılan tüm çağrılar aynı bağlantı hattı 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 bir PolicyRegistryile kaydetmektir. Ö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");

Yukarıdaki kodda:

  • polly kayıt defterine ve Longolmak üzere iki ilke Regular eklenir.
  • AddPolicyHandlerFromRegistry tek tek adlandırılmış istemcileri Polly kayıt defterinden bu ilkeleri kullanacak şekilde yapılandırıyor.

ve Polly tümleştirmeleri hakkında IHttpClientFactory daha fazla bilgi için Polly wiki'sine bakın.

HttpClient ve yaşam süresi yönetimi

üzerinde IHttpClientFactoryher CreateClient çağrıldığında yeni HttpClient bir örnek döndürülür. Adlandırılmış HttpMessageHandler istemci başına bir oluşturulur. Fabrika, örneklerin yaşam sürelerini HttpMessageHandler yönetir.

IHttpClientFactoryHttpMessageHandler kaynak tüketimini azaltmak için fabrika tarafından oluşturulan örnekleri havuza alır. HttpMessageHandler Yaşam süresi dolmamışsa yeni HttpClient bir örnek oluşturulurken havuzdaki örnek yeniden kullanılabilir.

her işleyici genellikle kendi temel HTTP bağlantılarını yönettiğinden işleyicilerin havuzalanması 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 yaşam süresi 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 örnekler genellikle atılması gerekmeyen .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 atılır.

Tek HttpClient bir örneği uzun süre canlı tutmak, başlamadan 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 şu işlemleri önler:

  • Havuz örneklerine göre HttpMessageHandler kaynak tükenme sorunları.
  • Örnekleri düzenli aralıklarla döngüye alarak HttpMessageHandler eski DNS sorunları.

Uzun ömürlü SocketsHttpHandler bir örnek kullanarak önceki sorunları çözmenin 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.

  • Bağlantılar SocketsHttpHandler örnekler arasında HttpClient paylaşılır. Bu paylaşım yuva tükenmesini önler.
  • Eski SocketsHttpHandler DNS sorunlarından kaçınmak için PooledConnectionLifetime bağlantıları öğesine göre döngüye alır.

Günlüğe Kaydetme

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, iletiler işlem hattındaki diğer işleyiciler tarafından işlenmeden önce 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ının içinde de gerçekleşir. MyNamedClient örneğinde, bu iletiler "System.Net.Http.HttpClient. 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 incelenmesini 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 ederek belirli adlandırılmış istemciler için günlük filtrelemeyi etkinleştirir.

HttpMessageHandler'ı yapılandırma

İstemci tarafından kullanılan iç HttpMessageHandler yapılandırmayı denetlemek gerekebilir.

adlandırılmış veya yazılan istemciler eklenirken bir IHttpClientBuilder döndürülür. ConfigurePrimaryHttpMessageHandler Uzantı yöntemi bir temsilci tanımlamak için kullanılabilir. Temsilci, bu istemci tarafından kullanılan birincili HttpMessageHandler 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 edilemeyen CookieContainer nesne paylaşımı genellikle yanlış kodla sonuçılır. gerektiren uygulamalar cookieiç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:

  • IHttpClientFactory ve GitHubServiceGenel Konağın hizmet kapsayıcısında kayıtlıdır.
  • GitHubService DI'den istenir ve bu da örneğini IHttpClientFactorytalep eder.
  • GitHubService , IHttpClientFactory belgeleri GitHub dallarını almak için kullandığı bir örneğini HttpClientoluşturmak için kullanır.
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 bir ara yazılımdır. Üst bilgi yaymayı 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 örnekleri 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 HttpClienthale getirir. içinde işleyicileri temsilci 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ürelerini el ile yönetirken 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 başlığı sürümündeki örnek kod, HTTP yanıtlarında döndürülen ON içeriğini seri durumdan çıkarma JSamacıyla kullanırSystem.Text.Json. ve ReadAsAsync<T>kullanan Json.NET örnekler için bu konunun 2.x sürümünü seçmek için sürüm seçiciyi kullanın.

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ısı AddHttpClientyapılarak kaydedilebilir:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpClient();
        // Remaining code deleted for brevity.

IHttpClientFactorybağımlılık ekleme (DI) kullanılarak istenebilir. Aşağıdaki kod bir HttpClient örnek 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 Önceki örnekte like kullanmak, mevcut bir uygulamayı yeniden düzenlemenin iyi bir yoludur. Bunun nasıl HttpClient kullanıldığı üzerinde 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.
  • Birç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ığı yukarıdaki 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 HttpClient örneği oluşturulur.
  • Yapılandırma eylemi çağrılır.

Adlandırılmış istemci oluşturmak için adını içine CreateClientgeçirin:

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 ana bilgisayar 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.
  • Yapılandırmak ve belirli HttpClientbir ile etkileşime geçmek için tek bir konum sağlayın. Örneğin, tek bir türü yazılan 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.

Türü yazılan istemci, oluşturucusunda bir HttpClient parametre 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");
    }
}

Yukarıdaki kodda:

  • Yapılandırma türü belirtilen istemciye taşınır.
  • HttpClient nesnesi ortak bir özellik olarak kullanıma sunulur.

İşlevselliği kullanıma HttpClient sunan 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 AddHttpClient , türü yazılan bir istemci sınıfını kaydetmek için çağrısı Startup.ConfigureServices yapar:

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şturucusunun bir örneğine geçirerek öğesinin HttpClient bir örneğini 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");
});

türü HttpClient 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

IHttpClientFactoryRefit 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ı 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ımlanan 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 de dahil olmak üzere diğer HTTP fiillerini destekler:

  • POST
  • PUT
  • DELETE
  • YAMA

Desteklenen HTTP fiillerinin tam listesi için bkz HttpMethod. .

Aşağıdaki örnekte HTTP POST isteği oluşturma adımları 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:

HttpClient 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();
}

Yukarıdaki kodda yöntemi çağrısında bulunur DeleteItemAsyncDeleteAsync. 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ğlanabilecek işleyicileri temsilci olarak belirleme kavramına sahiptir. IHttpClientFactory:

  • Her adlandırılmış 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 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 belirleme 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 döndürülür BadRequest .

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 istek yürütene kadar sonraki işleyiciyi sarmalar:

services.AddTransient<SecureRequestHandler>();
services.AddTransient<RequestDataHandler>();

services.AddHttpClient("clientwithhandlers")
    // This handler is on the outside and called first during the 
    // request, last during the response.
    .AddHttpMessageHandler<SecureRequestHandler>()
    // This handler is on the inside, closest to the request being 
    // sent.
    .AddHttpMessageHandler<RequestDataHandler>();

Giden istek ara yazılımında DI kullanma

IHttpClientFactory Yeni bir temsilci seçme işleyicisi oluşturduğunda, işleyicinin oluşturucu parametrelerini yerine getirmek için DI kullanır. IHttpClientFactory her işleyici için ayrı bir DI kapsamı oluşturur ve bu da işleyici kapsamı belirlenmiş bir hizmeti 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, IOperationScopedkapsamlı 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 belirleme 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 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 kapsamdaki hizmetlere bağımlı 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ı, istemcilere Polly tabanlı işleyiciler eklemeyi destekler. Polly için Microsoft.Extensions.Http.Polly NuGet paketi gerekir.

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ını sağlar. 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 deneniyor.

İ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 tüm HTTP yöntemleri için 30 saniyelik 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 deneme sırayla gerçekleşirse 30 saniye boyunca başka dış istekler engellenir. Devre kesici ilkeleri durum bilgisi olan ilkelerdir. Bu istemci aracılığıyla yapılan tüm çağrılar aynı bağlantı hattı 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 bir PolicyRegistryile kaydetmektir.

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. Adlandırılmış HttpMessageHandler istemci başına bir oluşturulur. Fabrika, örneklerin yaşam sürelerini HttpMessageHandler yönetir.

IHttpClientFactoryHttpMessageHandler kaynak tüketimini azaltmak için fabrika tarafından oluşturulan örnekleri havuza alır. HttpMessageHandler Yaşam süresi dolmamışsa yeni HttpClient bir örnek oluşturulurken havuzdaki örnek yeniden kullanılabilir.

her işleyici genellikle kendi temel HTTP bağlantılarını yönettiğinden işleyicilerin havuzalanması 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 yaşam süresi 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 örnekler genellikle atılması gerekmeyen .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 atılır.

Tek HttpClient bir örneği uzun süre canlı tutmak, başlamadan 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 şu işlemleri önler:

  • Havuz örneklerine göre HttpMessageHandler kaynak tükenme sorunları.
  • Örnekleri düzenli aralıklarla döngüye alarak HttpMessageHandler eski DNS sorunları.

Uzun ömürlü SocketsHttpHandler bir örnek kullanarak önceki sorunları çözmenin 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.

  • Bağlantılar SocketsHttpHandler örnekler arasında HttpClient paylaşılır. Bu paylaşım yuva tükenmesini önler.
  • Eski SocketsHttpHandler DNS sorunlarından kaçınmak için PooledConnectionLifetime bağlantıları öğesine göre döngüye alır.

CookieS

Havuza alınan HttpMessageHandler örnekler, nesnelerin paylaşılmasıyla CookieContainer sonuçlanır. Tahmin edilemeyen CookieContainer nesne paylaşımı genellikle yanlış kodla sonuçılır. gerektiren uygulamalar cookieiç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üğe Kaydetme

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, iletiler işlem hattındaki diğer işleyiciler tarafından işlenmeden önce 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ının içinde de gerçekleşir. MyNamedClient örneğinde, bu iletiler "System.Net.Http.HttpClient. 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 incelenmesini 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 ederek belirli adlandırılmış istemciler için günlük filtrelemeyi etkinleştirir.

HttpMessageHandler'ı yapılandırma

İstemci tarafından kullanılan iç HttpMessageHandler yapılandırmayı denetlemek gerekebilir.

adlandırılmış veya yazılan istemciler eklenirken bir IHttpClientBuilder döndürülür. ConfigurePrimaryHttpMessageHandler Uzantı yöntemi bir temsilci tanımlamak için kullanılabilir. Temsilci, bu istemci tarafından kullanılan birincili HttpMessageHandler 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 bu örnek 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 ASP.NET Core bir ara yazılımdır. Üst bilgi yaymayı 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 örnekleri 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 HttpClienthale getirir. içinde işleyicileri temsilci 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ürelerini el ile yönetirken 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 başlığı sürümündeki örnek kod, HTTP yanıtlarında döndürülen ON içeriğini seri durumdan çıkarma JSamacıyla kullanırSystem.Text.Json. ve ReadAsAsync<T>kullanan Json.NET örnekler için bu konunun 2.x sürümünü seçmek için sürüm seçiciyi kullanın.

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ısı AddHttpClientyapılarak kaydedilebilir:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpClient();
        // Remaining code deleted for brevity.

IHttpClientFactorybağımlılık ekleme (DI) kullanılarak istenebilir. Aşağıdaki kod bir HttpClient örnek 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 Önceki örnekte like kullanmak, mevcut bir uygulamayı yeniden düzenlemenin iyi bir yoludur. Bunun nasıl HttpClient kullanıldığı üzerinde 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.
  • Birç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ığı yukarıdaki 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 HttpClient örneği oluşturulur.
  • Yapılandırma eylemi çağrılır.

Adlandırılmış istemci oluşturmak için adını içine CreateClientgeçirin:

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 ana bilgisayar 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.
  • Yapılandırmak ve belirli HttpClientbir ile etkileşime geçmek için tek bir konum sağlayın. Örneğin, tek bir türü yazılan 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.

Türü yazılan istemci, oluşturucusunda bir HttpClient parametre 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 çevrildiğini görmek istiyorsanız , bu GitHub tartışma sorununda bize bildirin.

Yukarıdaki kodda:

  • Yapılandırma türü belirtilen istemciye taşınır.
  • HttpClient nesnesi ortak bir özellik olarak kullanıma sunulur.

İşlevselliği kullanıma HttpClient sunan 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 AddHttpClient , türü yazılan bir istemci sınıfını kaydetmek için çağrısı Startup.ConfigureServices yapar:

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şturucusunun bir örneğine geçirerek öğesinin HttpClient bir örneğini 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");
});

türü HttpClient 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

IHttpClientFactoryRefit 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ı 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ımlanan 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 de dahil olmak üzere diğer HTTP fiillerini destekler:

  • POST
  • PUT
  • DELETE
  • YAMA

Desteklenen HTTP fiillerinin tam listesi için bkz HttpMethod. .

Aşağıdaki örnekte HTTP POST isteği oluşturma adımları 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:

HttpClient 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();
}

Yukarıdaki kodda yöntemi çağrısında bulunur DeleteItemAsyncDeleteAsync. 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ğlanabilecek işleyicileri temsilci olarak belirleme kavramına sahiptir. IHttpClientFactory:

  • Her adlandırılmış 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 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 belirleme 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 döndürülür BadRequest .

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 istek yürütene kadar sonraki işleyiciyi sarmalar:

services.AddTransient<SecureRequestHandler>();
services.AddTransient<RequestDataHandler>();

services.AddHttpClient("clientwithhandlers")
    // This handler is on the outside and called first during the 
    // request, last during the response.
    .AddHttpMessageHandler<SecureRequestHandler>()
    // This handler is on the inside, closest to the request being 
    // sent.
    .AddHttpMessageHandler<RequestDataHandler>();

Giden istek ara yazılımında DI kullanma

IHttpClientFactory Yeni bir temsilci seçme işleyicisi oluşturduğunda, işleyicinin oluşturucu parametrelerini yerine getirmek için DI kullanır. IHttpClientFactory her işleyici için ayrı bir DI kapsamı oluşturur ve bu da işleyici kapsamı belirlenmiş bir hizmeti 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, IOperationScopedkapsamlı 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 belirleme 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 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 kapsamdaki hizmetlere bağımlı 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ı, istemcilere Polly tabanlı işleyiciler eklemeyi destekler. Polly için Microsoft.Extensions.Http.Polly NuGet paketi gerekir.

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ını sağlar. 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 deneniyor.

İ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 tüm HTTP yöntemleri için 30 saniyelik 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 deneme sırayla gerçekleşirse 30 saniye boyunca başka dış istekler engellenir. Devre kesici ilkeleri durum bilgisi olan ilkelerdir. Bu istemci aracılığıyla yapılan tüm çağrılar aynı bağlantı hattı 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 bir PolicyRegistryile kaydetmektir.

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. Adlandırılmış HttpMessageHandler istemci başına bir oluşturulur. Fabrika, örneklerin yaşam sürelerini HttpMessageHandler yönetir.

IHttpClientFactoryHttpMessageHandler kaynak tüketimini azaltmak için fabrika tarafından oluşturulan örnekleri havuza alır. HttpMessageHandler Yaşam süresi dolmamışsa yeni HttpClient bir örnek oluşturulurken havuzdaki örnek yeniden kullanılabilir.

her işleyici genellikle kendi temel HTTP bağlantılarını yönettiğinden işleyicilerin havuzalanması 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 yaşam süresi 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 örnekler genellikle atılması gerekmeyen .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 atılır.

Tek HttpClient bir örneği uzun süre canlı tutmak, başlamadan 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 şu işlemleri önler:

  • Havuz örneklerine göre HttpMessageHandler kaynak tükenme sorunları.
  • Örnekleri düzenli aralıklarla döngüye alarak HttpMessageHandler eski DNS sorunları.

Uzun ömürlü SocketsHttpHandler bir örnek kullanarak önceki sorunları çözmenin 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.

  • Bağlantılar SocketsHttpHandler örnekler arasında HttpClient paylaşılır. Bu paylaşım yuva tükenmesini önler.
  • Eski SocketsHttpHandler DNS sorunlarından kaçınmak için PooledConnectionLifetime bağlantıları öğesine göre döngüye alır.

CookieS

Havuza alınan HttpMessageHandler örnekler, nesnelerin paylaşılmasıyla CookieContainer sonuçlanır. Tahmin edilemeyen CookieContainer nesne paylaşımı genellikle yanlış kodla sonuçılır. gerektiren uygulamalar cookieiç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üğe Kaydetme

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, iletiler işlem hattındaki diğer işleyiciler tarafından işlenmeden önce 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ının içinde de gerçekleşir. MyNamedClient örneğinde, bu iletiler "System.Net.Http.HttpClient. 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 incelenmesini 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 ederek belirli adlandırılmış istemciler için günlük filtrelemeyi etkinleştirir.

HttpMessageHandler'ı yapılandırma

İstemci tarafından kullanılan iç HttpMessageHandler yapılandırmayı denetlemek gerekebilir.

adlandırılmış veya yazılan istemciler eklenirken bir IHttpClientBuilder döndürülür. ConfigurePrimaryHttpMessageHandler Uzantı yöntemi bir temsilci tanımlamak için kullanılabilir. Temsilci, bu istemci tarafından kullanılan birincili HttpMessageHandler 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 bu örnek 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 ASP.NET Core bir ara yazılımdır. Üst bilgi yaymayı 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 örnekleri 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.
  • içinde işleyicileri HttpClient temsilci olarak atama yoluyla giden ara yazılım kavramını uyumlu hale getirir ve Bundan yararlanmak için Polly tabanlı ara yazılım için uzantılar sağlar.
  • Yaşam sürelerini el ile yönetirken HttpClient oluşan yaygın DNS sorunlarını önlemek için temel HttpClientMessageHandler alınan örneklerin havuzunu ve ömrünü yönetir.
  • Fabrika tarafından oluşturulan istemciler aracılığıyla gönderilen tüm istekler için yapılandırılabilir bir günlük deneyimi (aracılığıyla ILogger) ekler.

Örnek kodu görüntüleme veya indirme (indirme)

Önkoşullar

.NET Framework hedefleyen projeler için Microsoft.Extensions.Http NuGet paketinin yüklenmesi gerekir. .NET Core'ı hedefleyen ve Microsoft.AspNetCore.App meta paketine başvuran projeler zaten paketi içerir Microsoft.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çindeki 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 şu anda oluşturulduğu yerlerde HttpClient , 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 kullanmak bir seçenektir. 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ışında 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 bir dize parametresi geçirilebilir CreateClient. 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 ana bilgisayar 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.
  • Yapılandırmak ve belirli HttpClientbir 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 gerektiğinde uygulamanıza eklenebilir.

Türü yazılan istemci, oluşturucusunda bir HttpClient parametre 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;
    }
}

Önceki kodda, yapılandırma türü belirtilen istemciye taşınır. HttpClient nesnesi ortak 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 bir 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 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");
});

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önteminden GetRepos geçer.

Oluşturulan istemciler

IHttpClientFactoryRefit 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ı 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ımlanan 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ğlanabilecek 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 kaydedilmesini ve zincirlenmesi desteklemektedir. Bu işleyicilerin her biri giden istek öncesinde ve sonrasında iş gerçekleştirebilir. Bu düzen, ASP.NET Core 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, bir için yapılandırmaya 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 DI'de geçici bir hizmet olarak kaydedilmesi gerekir, hiçbir zaman kapsamı belirtilmemelidir . İşleyici kapsamlı bir hizmet olarak kaydedildiyse ve işleyicinin bağımlı olduğu hizmetler atılabilirse:

  • İşleyicinin hizmetleri, işleyici kapsam 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 istek 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şleyicisine veri geçirin.
  • 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

Çoğu yaygın hata, dış HTTP çağrıları geçici olduğunda oluşur. Geçici hataları işlemek için ilkenin tanımlanmasını sağlayan adlı AddTransientHttpErrorPolicy kullanışlı bir uzantı yöntemi eklenmiştir. Bu uzantı yöntemiyle yapılandırılan ilkeler, HTTP 5xx yanıtlarını ve HTTP 408 yanıtlarını işler HttpRequestException.

Uzantı içinde AddTransientHttpErrorPolicyStartup.ConfigureServiceskullanılabilir. Uzantı, olası geçici bir PolicyBuilder 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 deneniyor.

İ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 denetlenmesini 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 tüm HTTP yöntemleri için 30 saniyelik 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 uzantıyı AddTransientHttpErrorPolicy kullanarak yeniden deneme ilkesi ekler. Başarısız istekler en fazla üç kez yeniden deneniyor. İkinci çağrısı AddTransientHttpErrorPolicy bir devre kesici ilkesi ekler. Beş başarısız deneme sırayla gerçekleşirse 30 saniye boyunca başka dış istekler engellenir. Devre kesici ilkeleri durum bilgisi olan ilkelerdir. Bu istemci aracılığıyla yapılan tüm çağrılar aynı bağlantı hattı 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 bir PolicyRegistryile kaydetmektir. İşleyicinin kayıt defterinden bir ilke kullanılarak eklenmesini sağlayan bir uzantı yöntemi sağlanır:

var registry = services.AddPolicyRegistry();

registry.Add("regular", timeout);
registry.Add("long", longTimeout);

services.AddHttpClient("regulartimeouthandler")
    .AddPolicyHandlerFromRegistry("regular");

Yukarıdaki kodda, öğesine eklendiğinde 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 bilgi Polly wiki'sinde bulunabilir.

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 sürelerini HttpMessageHandler yönetir.

IHttpClientFactoryHttpMessageHandler kaynak tüketimini azaltmak için fabrika tarafından oluşturulan örnekleri havuza alır. HttpMessageHandler Yaşam süresi dolmamışsa yeni HttpClient bir örnek oluşturulurken havuzdaki örnek yeniden kullanılabilir.

her işleyici genellikle kendi temel HTTP bağlantılarını yönettiğinden işleyicilerin havuzalanması tercih edilir. Gerekenden daha fazla işleyici oluşturmak bağlantı gecikmelerine neden olabilir. Bazı işleyiciler 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 yaşam süresi 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 üzerinde öğesini çağırın SetHandlerLifetimeIHttpClientBuilder :

services.AddHttpClient("extendedhandlerlifetime")
    .SetHandlerLifetime(TimeSpan.FromMinutes(5));

İstemcinin atı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 atılır. Örnekler HttpClient genellikle atılması gerekmeyen .NET nesneleri olarak kabul edilebilir.

Tek HttpClient bir örneği uzun süre canlı tutmak, başlamadan 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 şu işlemleri önler:

  • Havuz örneklerine göre HttpMessageHandler kaynak tükenme sorunları.
  • Örnekleri düzenli aralıklarla döngüye alarak HttpMessageHandler eski DNS sorunları.

Uzun ömürlü SocketsHttpHandler bir örnek kullanarak önceki sorunları çözmenin 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.

  • Bağlantılar SocketsHttpHandler örnekler arasında HttpClient paylaşılır. Bu paylaşım yuva tükenmesini önler.
  • Eski SocketsHttpHandler DNS sorunlarından kaçınmak için PooledConnectionLifetime bağlantıları öğesine göre döngüye alır.

CookieS

Havuza alınan HttpMessageHandler örnekler, nesnelerin paylaşılmasıyla CookieContainer sonuçlanır. Tahmin edilemeyen CookieContainer nesne paylaşımı genellikle yanlış kodla sonuçılır. gerektiren uygulamalar cookieiç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üğe Kaydetme

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, iletiler işlem hattındaki diğer işleyiciler tarafından işlenmeden önce 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ının içinde de 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 incelenmesini 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 ederek, 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 yapılandırmayı denetlemek gerekebilir.

adlandırılmış veya yazılan istemciler eklenirken bir IHttpClientBuilder döndürülür. ConfigurePrimaryHttpMessageHandler Uzantı yöntemi bir temsilci tanımlamak için kullanılabilir. Temsilci, bu istemci tarafından kullanılan birincili HttpMessageHandler 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:

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 yaymayı 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