ASP.NET Core 'de ıhttpclientfactory kullanarak HTTP istekleri yapın

Kirk Larkabağı, Steve Gordon, Glenn CONDRONve Ryan şimdi ak.

Bir IHttpClientFactory uygulamadaki örnekleri yapılandırmak ve oluşturmak için kayıt yapılabilir ve kullanılabilir HttpClient . IHttpClientFactory aşağıdaki avantajları sunar:

  • , Mantıksal örnekleri adlandırmak ve yapılandırmak için merkezi bir konum sağlar HttpClient . Örneğin, GitHub adlı bir istemci, GitHuberişmek için kaydedilebilir ve yapılandırılabilir. Varsayılan istemci, genel erişim için kaydedilebilir.
  • ' De işleyiciler temsilci seçme yoluyla giden ara yazılım kavramını daha da artırır HttpClient . ' De işleyiciler temsilci atama avantajlarından faydalanmak için, Polya tabanlı bir ara yazılım için uzantılar sağlar HttpClient .
  • Temel örneklerin biriktirmesini ve ömrünü yönetir HttpClientMessageHandler . Otomatik yönetim, yaşam sürelerini el ile yönetirken oluşan ortak DNS (etki alanı adı sistemi) sorunlarını önler HttpClient .
  • ILoggerFabrika tarafından oluşturulan istemciler aracılığıyla gönderilen tüm istekler için yapılandırılabilir bir günlüğe kaydetme deneyimi ekler (aracılığıyla).

Örnek kodu görüntüleyin veya indirin (nasıl indirilir).

Bu konu sürümündeki örnek kod, System.Text.Json http yanıtlarında döndürülen JSON içeriğinin serisini kaldırmak için kullanır. Ve kullanan örnekler için Json.NET ReadAsAsync<T> , bu konunun 2. x sürümünü seçmek üzere sürüm seçiciyi kullanın.

Tüketim desenleri

Bir uygulamada çeşitli yollar IHttpClientFactory kullanılabilir:

En iyi yaklaşım, uygulamanın gereksinimlerine bağlı olarak değişir.

Temel kullanım

IHttpClientFactory , çağırarak kaydedilebilir AddHttpClient :

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.

IHttpClientFactory Bağımlılık ekleme (dı)kullanılarak bir istek yapılabilir. Aşağıdaki kod IHttpClientFactory bir örnek oluşturmak için kullanır HttpClient :

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

IHttpClientFactoryYukarıdaki örnekte olduğu gibi kullanmak, mevcut bir uygulamayı yeniden düzenleme için iyi bir yoldur. Kullanım hakkında hiçbir etkisi yoktur HttpClient . HttpClientVar olan bir uygulamada örneklerin oluşturulduğu yerlerde, bu oluşumların ' i çağrılarıyla değiştirin CreateClient .

Adlandırılmış istemciler

Adlandırılmış istemciler şu durumlarda iyi bir seçimdir:

  • Uygulama birçok farklı kullanımı gerektirir HttpClient .
  • Birçok HttpClient s farklı yapılandırmaya sahiptir.

Adlandırılmış için yapılandırma HttpClient , içinde kayıt sırasında belirtilebilir Startup.ConfigureServices :

services.AddHttpClient("github", c =>
{
    c.BaseAddress = new Uri("https://api.github.com/");
    // Github API versioning
    c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    // Github requires a user-agent
    c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});

İstemcinin yapılandırıldığı önceki kodda:

  • Temel adres https://api.github.com/ .
  • GitHub apı ile çalışmak için iki üst bilgi gereklidir.

CreateClient

Her zaman CreateClient çağrılır:

  • Yeni bir örneği HttpClient oluşturulur.
  • Yapılandırma eylemi çağrılır.

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

public class NamedClientModel : PageModel
{
    private readonly IHttpClientFactory _clientFactory;

    public IEnumerable<GitHubPullRequest> PullRequests { get; private set; }

    public bool GetPullRequestsError { get; private set; }

    public bool HasPullRequests => PullRequests.Any();

    public NamedClientModel(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }

    public async Task OnGet()
    {
        var request = new HttpRequestMessage(HttpMethod.Get,
            "repos/dotnet/AspNetCore.Docs/pulls");

        var client = _clientFactory.CreateClient("github");

        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            using var responseStream = await response.Content.ReadAsStreamAsync();
            PullRequests = await JsonSerializer.DeserializeAsync
                    <IEnumerable<GitHubPullRequest>>(responseStream);
        }
        else
        {
            GetPullRequestsError = true;
            PullRequests = Array.Empty<GitHubPullRequest>();
        }
    }
}

Yukarıdaki kodda, isteğin bir ana bilgisayar adı belirtmesi gerekmez. İstemci için yapılandırılan taban adresi kullanıldığından, kod yalnızca yolu geçirebilir.

Yazılan istemciler

Yazılan istemciler:

  • Dizeleri anahtar olarak kullanma gereksinimi olmadan, adlandırılmış istemcilerle aynı özellikleri sağlayın.
  • İstemcileri tükettiren IntelliSense ve derleyici yardımı sağlar.
  • Yapılandırmak ve belirli bir ile etkileşimde bulunmak için tek bir konum belirtin HttpClient . Örneğin, tek bir türü belirtilmiş istemci kullanılabilir:
    • Tek bir arka uç uç noktası için.
    • Uç nokta ile ilgili tüm mantığı kapsüllemek için.
  • DI ile birlikte çalışın ve uygulamada gerektiğinde eklenebilir.

Türü belirtilmiş istemci HttpClient , oluşturucusunda bir parametreyi kabul eder:

public class GitHubService
{
    public HttpClient Client { get; }

    public GitHubService(HttpClient client)
    {
        client.BaseAddress = new Uri("https://api.github.com/");
        // GitHub API versioning
        client.DefaultRequestHeaders.Add("Accept",
            "application/vnd.github.v3+json");
        // GitHub requires a user-agent
        client.DefaultRequestHeaders.Add("User-Agent",
            "HttpClientFactory-Sample");

        Client = client;
    }

    public async Task<IEnumerable<GitHubIssue>> GetAspNetDocsIssues()
    {
        return await Client.GetFromJsonAsync<IEnumerable<GitHubIssue>>(
          "/repos/aspnet/AspNetCore.Docs/issues?state=open&sort=created&direction=desc");
    }
}

Yukarıdaki kodda:

  • Yapılandırma, yazılan istemciye taşınır.
  • HttpClientNesne bir ortak özellik olarak sunulur.

İşlevselliği kullanıma sunan, API 'ye özgü Yöntemler oluşturulabilir HttpClient . Örneğin, GetAspNetDocsIssues yöntemi açık sorunları almak için kodu kapsüller.

Aşağıdaki kod, AddHttpClient Startup.ConfigureServices türü belirtilmiş bir istemci sınıfını kaydetmek için ' de çağırır:

services.AddHttpClient<GitHubService>();

Yazılan istemci, DI ile geçici olarak kaydedilir. Yukarıdaki kodda AddHttpClient GitHubService geçici bir hizmet olarak kaydedilir. Bu kayıt, için bir fabrika yöntemi kullanır:

  1. HttpClient örneği oluşturun.
  2. Örneğini oluşturucusuna geçirerek bir örneğini oluşturun GitHubService HttpClient .

Yazılan istemci doğrudan eklenebilir ve tüketilebilir:

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ı Startup.ConfigureServices , türü belirlenmiş istemcinin Oluşturucusu yerine kayıt sırasında belirtilebilir:

services.AddHttpClient<RepoService>(c =>
{
    c.BaseAddress = new Uri("https://api.github.com/");
    c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});

, HttpClient Türü belirlenmiş bir istemci içinde kapsüllenebilir. Bunu bir özellik olarak göstermek yerine, örneği dahili olarak çağıran bir yöntem tanımlayın HttpClient :

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 bir özel alanda depolanır. Öğesine erişimi, HttpClient genel yöntemi tarafından yapılır GetRepos .

Oluşturulan istemciler

IHttpClientFactory , yeniden sığdırmagibi üçüncü taraf kitaplıklarla birlikte kullanılabilir. Yeniden sığdırma, .NET için bir REST kitaplığıdır. REST API 'Leri canlı arabirimlere dönüştürür. Arabirim bir uygulama, RestService HttpClient dış http çağrıları yapmak için kullanılarak tarafından dinamik olarak oluşturulur.

Bir arabirim ve yanıt, dış API 'yi ve yanıtını temsil edecek şekilde tanımlanır:

public interface IHelloClient
{
    [Get("/helloworld")]
    Task<Reply> GetMessageAsync();
}

public class Reply
{
    public string Message { get; set; }
}

Türü belirlenmiş bir istemci eklenebilir, uygulamayı oluşturmak için yeniden sığdırma kullanımı kullanılabilir:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient("hello", c =>
    {
        c.BaseAddress = new Uri("http://localhost:5000");
    })
    .AddTypedClient(c => Refit.RestService.For<IHelloClient>(c));

    services.AddControllers();
}

Tanımlı arabirim, gereken yerde, mak ve Refit tarafından sağlanmış uygulama ile 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();
    }
}

GÖNDERI, PUT ve DELETE isteklerini oluşturma

Yukarıdaki örneklerde, tüm HTTP istekleri GET HTTP fiilini kullanır. HttpClient , aşağıdakiler de dahil olmak üzere diğer HTTP fiillerini de destekler:

  • POST
  • PUT
  • DELETE
  • DÜZELTMESI

Desteklenen HTTP fiillerinin tüm listesi için bkz HttpMethod ..

Aşağıdaki örnek, bir HTTP POST isteğinin nasıl yapılacağını göstermektedir:

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:

  • TodoItemParametresini kullanarak JSON için parametreyi seri hale getirir System.Text.Json . Bu JsonSerializerOptions , serileştirme işlemini yapılandırmak için bir örneğini kullanır.
  • StringContentHttp isteğinin gövdesinde göndermek üzere seri hale GETIRILMIŞ JSON paketini paketlemek için bir örneği oluşturur.
  • PostAsyncJSON içeriğini BELIRTILEN URL 'ye göndermek için çağrılar. Bu, HttpClient. BaseAddress'e eklenen GÖRELI bir URL 'dir.
  • EnsureSuccessStatusCodeYanıt durum kodu başarıyı belirtmezse bir özel durum oluşturmak için çağırır.

HttpClient , diğer içerik türlerini de destekler. Örneğin MultipartContent ve StreamContent. Desteklenen içeriğin tamamı listesi için bkz HttpContent ..

Aşağıdaki örnekte bir HTTP PUT isteği gösterir:

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. yöntemi SaveItemAsync yerine PutAsync yöntemini PostAsync çağıran.

Aşağıdaki örnekte bir HTTP DELETE isteği gösterir:

public async Task DeleteItemAsync(long itemId)
{
    using var httpResponse =
        await _httpClient.DeleteAsync($"/api/TodoItems/{itemId}");

    httpResponse.EnsureSuccessStatusCode();
}

Yukarıdaki kodda yöntemi DeleteItemAsync çağrısında DeleteAsync dır. HTTP DELETE istekleri genellikle gövde içermeyer, yöntemi bir örneğini kabul eden DeleteAsync bir aşırı yükleme HttpContent sağlamaz.

ile farklı HTTP fiilleri kullanma hakkında daha fazla bilgi edinmek HttpClient için bkz. HttpClient .

Giden istek ara yazılımı

HttpClient , giden HTTP istekleri için birbirine bağlanacak işleyiciler için delegelik kavramına sahip. IHttpClientFactory:

  • Adlandırılmış her istemciye uygulanacak işleyicileri tanımlamayı basitler.

  • Giden istek ara yazılım işlem hattı oluşturmak için birden çok işleyicinin kaydını ve zincirlesini destekler. Bu işleyicilerin her biri, giden istekten önce ve sonra iş gerçekleştire bir işleyicidir. Bu düzen:

    • , ağ trafiğinde gelen ara yazılım işlem hattına ASP.NET Core.
    • HTTP istekleriyle ilgili çapraz kesme endişelerini yönetmek için bir mekanizma sağlar, örneğin:
      • Önbelleğe alma
      • hata işleme
      • Seri -leştirme
      • günlüğe kaydetme

Bir işleyiciyi tapan oluşturmak için:

  • 'den DelegatingHandler türetin.
  • geçersiz SendAsync kılın. İsteği işlem hattındaki sonraki işleyiciye geçirmeden önce kodu yürütü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, üst X-API-KEY bilginin istekte olup olduğunu denetler. Eksikse X-API-KEY BadRequest döndürülür.

ile yapılandırmasına birden fazla işleyici HttpClient Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandler 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 kaydedilmiştir. Kaydedildiktan AddHttpMessageHandler sonra, işleyicinin türünü geçerek çağrıl olabilir.

Birden çok işleyici, yürütmeleri gereken sırayla kayded olabilir. Son işleyici isteği yürütene kadar her işleyici HttpClientHandler 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

Yeni IHttpClientFactory bir delegating 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 kapsamlı bir hizmet tükettiği zaman şaşırtıcı davranışlara yol açabilirsiniz.

Örneğin, tanımlayıcıya sahip bir işlem olarak bir görevi temsil eden aşağıdaki arabirimini ve uygulamasını göz önünde OperationId bulundurabilirsiniz:

public interface IOperationScoped 
{
    string OperationId { get; }
}

public class OperationScoped : IOperationScoped
{
    public string OperationId { get; } = Guid.NewGuid().ToString()[^4..];
}

Adı da anlaşılacağı IOperationScoped gibi, kapsamlı bir yaşam süresi kullanılarak DI'ye kaydedilir:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<TodoContext>(options =>
        options.UseInMemoryDatabase("TodoItems"));

    services.AddHttpContextAccessor();

    services.AddHttpClient<TodoClient>((sp, httpClient) =>
    {
        var httpRequest = sp.GetRequiredService<IHttpContextAccessor>().HttpContext.Request;

        // For sample purposes, assume TodoClient is used in the context of an incoming request.
        httpClient.BaseAddress = new Uri(UriHelper.BuildAbsolute(httpRequest.Scheme,
                                         httpRequest.Host, httpRequest.PathBase));
        httpClient.Timeout = TimeSpan.FromSeconds(5);
    });

    services.AddScoped<IOperationScoped, OperationScoped>();
    
    services.AddTransient<OperationHandler>();
    services.AddTransient<OperationResponseHandler>();

    services.AddHttpClient("Operation")
        .AddHttpMessageHandler<OperationHandler>()
        .AddHttpMessageHandler<OperationResponseHandler>()
        .SetHandlerLifetime(TimeSpan.FromSeconds(5));

    services.AddControllers();
    services.AddRazorPages();
}

Aşağıdaki delegating işleyicisi, giden isteğin IOperationScoped üst bilgisini X-OPERATION-ID ayarlamak için kullanır:

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 [ HttpRequestsSample ] sayfasında](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/fundamentals/http-requests/samples/3.x/HttpRequestsSample)sayfasına gidin /Operation ve sayfayı yenileyin. İstek kapsamı değeri her istek için değişir, ancak işleyici kapsamı değeri yalnızca 5 saniyede bir değişir.

İşleyiciler herhangi bir kapsamda hizmetlere bağımlı olabilir. İşleyicinin bağımlı olduğu hizmetler, işleyici atılması sırasında atılması.

İ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, Polly üçüncü taraf kitaplığıyla tümleştirildi. Polly, .NET için kapsamlı bir resilians ve geçici hata işleme kitaplığıdır. Geliştiricilerin Retry, Circuit Breaker, Timeout, Bulkhead Isolation ve Fallback gibi ilkeleri akıcı ve iş parçacığı güvenli bir şekilde ifade etmelerini sağlar.

Uzantı yöntemleri, yapılandırılmış örneklerle Polly ilkelerinin kullanımını etkinleştirmek için HttpClient sağlanır. Polly uzantıları, istemcilere Polly tabanlı işleyiciler eklemeyi destekler. Polly, Microsoft.Extensions.Http.Polly NuGet gerektirir.

Geçici hataları işleme

Hatalar genellikle dış HTTP çağrıları geçici olduğunda oluşur. AddTransientHttpErrorPolicy , geçici hataları işlemek için bir ilkenin tanımlanmalıdır. ile yapılandırılan AddTransientHttpErrorPolicy ilkeler aşağıdaki yanıtları işlemektedir:

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.

Yukarıdaki kodda bir WaitAndRetryAsync ilke tanımlanmıştır. Başarısız istekler denemeler arasında 600 ms gecikmeyle üç kez yeniden denendi.

İlkeleri dinamik olarak seçme

Uzantı yöntemleri, Polly tabanlı işleyiciler eklemek için sağlanır, örneğin, AddPolicyHandler . Aşağıdaki aşırı AddPolicyHandler yükleme, hangi ilkenin uygulanacak olduğuna karar verme isteğini 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 bir zaman aşımı kullanılır.

Birden çok Polly işleyicisi ekleme

Polly ilkelerini iç içe yerleştirme yaygındı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 bir AddTransientHttpErrorPolicy yeniden deneme ilkesi eklemek için kullanır. Başarısız istekler en fazla üç kez yeniden denendi.
  • İkinci çağrı AddTransientHttpErrorPolicy bir devre kesici ilkesi ekler. 5 başarısız deneme sırayla gerçekleşirse 30 saniye daha dış istek engellenir. Devre kesici ilkeleri durum bilgilidir. 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 ile PolicyRegistry 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 IHttpClientFactory Polly tümleştirmeleri hakkında daha fazla bilgi için bkz. Polly wiki.

HttpClient ve yaşam süresi yönetimi

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

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

Her işleyici genellikle kendi temel HTTP bağlantılarını yönetirken işleyicilerin havuzu tercih edilir. Gerekenden daha fazla işleyici oluşturmak bağlantı gecikmeleri ile sonuçlandırabilirsiniz. Bazı işleyiciler ayrıca bağlantıları süresiz olarak açık tutmakta ve bu da işleyicinin DNS (Etki Alanı Adı Sistemi) değişikliklerine tepki vermesini önlemektedir.

Varsayılan işleyicinin ömrü iki dakikadır. Varsayılan değer, adlandırılmış istemci başına 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ı gerekmeden .NET nesneleri olarak kabul edilebilir. Atma, giden istekleri iptal eder ve HttpClient çağrıldikten sonra verilen örneğin kullanılamaz durumda olacağını garanti Dispose eder. IHttpClientFactory örnekler tarafından kullanılan kaynakları izler ve HttpClient atlar.

Tek bir örneği HttpClient uzun süre canlı tutmak, kullanılmaya başlamadan önce yaygın olarak kullanılan bir desendir. IHttpClientFactory bu düzen, 'a katıldıktan sonra gereksiz hale IHttpClientFactory gelir.

IHttpClientFactory'nin alternatifleri

IHttpClientFactoryDI özellikli bir uygulamada kullanmak şunları önler:

  • Örnekleri havuza alan kaynak tükenme HttpMessageHandler sorunları.
  • Düzenli aralıklarla örneklerin atarak eski DNS HttpMessageHandler sorunları.

Uzun süreli bir örneği kullanarak önceki sorunları çözmenin alternatif yolları SocketsHttpHandler vardır.

  • Uygulama başlatıldığında SocketsHttpHandler bir örneği oluşturun ve uygulamanın ömrü boyunca bunu kullanın.
  • DNS PooledConnectionLifetime yenileme zamanlarına göre uygun bir değere yapılandırma.
  • Gerektiğinde HttpClient kullanarak new HttpClient(handler, disposeHandler: false) örnekler oluşturun.

Yukarıdaki yaklaşımlar, kaynak yönetimi sorunlarını da IHttpClientFactory benzer şekilde çözer.

  • örnekler SocketsHttpHandler arasında bağlantı paylaşımı HttpClient sağlar. Bu paylaşım yuva tükenmesini önler.
  • Eski SocketsHttpHandler DNS sorunlarını önlemek için bağlantıları uygun şekilde PooledConnectionLifetime döngüye alar.

CookieS

Havuza alan HttpMessageHandler örnekler, nesnelerin CookieContainer paylaşılır olmasıyla sonuç verir. Beklentisiz nesne paylaşımı CookieContainer genellikle yanlış koda neden olur. gerektiren uygulamalar için cookie şu iki işlemden birini değerlendirin:

  • Otomatik işlemeyi devre cookie dışı bırakma
  • Kaçın -arak IHttpClientFactory

Otomatik ConfigurePrimaryHttpMessageHandler işlemeyi devre dışı bırakmak için cookie çağrısı:

services.AddHttpClient("configured-disable-automatic-cookies")
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        return new HttpClientHandler()
        {
            UseCookies = false,
        };
    });

Günlüğe Kaydetme

Tüm istekler IHttpClientFactory için kayıt günlüğü iletileriyle 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 bilgilerini günlüğe kaydetme gibi ek günlükler yalnızca izleme düzeyinde dahil edilir.

Her istemci için kullanılan günlük kategorisi istemcinin adını içerir. Örneğin MyNamedClient adlı bir istemci iletileri "System.Net.Http.HttpClient" kategorisiyle günlüğe kaydeder. MyNamedClient. LogicalHandler". LogicalHandler ile son ekli iletiler, istek işleyicisi işlem hattının dışında oluşur. İstekte iletiler, işlem hattında 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, istek işleyicisi işlem hattında da gerçekleşir. MyNamedClient örneğinde, bu iletiler "System.Net.Http.HttpClient" günlük kategorisiyle günlüğe kaydedilir. MyNamedClient. ClientHandler". İstek için, bu durum diğer tüm işleyiciler çalıştırktan ve istek gönderilmeden hemen önce gerçekleşir. Yanıtta bu günlük, işleyici işlem hattına geri dönmeden ö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 incesini sağlar. Bu, istek üst bilgisinde veya yanıt durum kodunda yapılan değişiklikleri içerebilir.

İstemcinin adını günlük kategorisine dahil olmak, belirli adlandırılmış istemciler için günlük filtrelemeyi sağlar.

HttpMessageHandler'ı yapılandırma

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

Adlandırılmış IHttpClientBuilder veya türe bağlı istemciler ekliyken bir döndürülür. Uzantı ConfigurePrimaryHttpMessageHandler 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

Bir konsol uygulamasında, aşağıdaki paket başvurularını projeye ekleyin:

Aşağıdaki örnekte:

  • IHttpClientFactory , Genel Ana Bilgisayar'ın hizmet kapsayıcısı içinde kayıtlıdır.
  • MyService , hizmetten bir oluşturmak için kullanılan bir istemci fabrika örneği HttpClient oluşturur. HttpClient bir web sayfasını almak için kullanılır.
  • Main hizmetin yöntemini yürütmek ve web sayfası içeriğinin ilk GetPage 500 karakteri konsola yazmak için bir kapsam oluşturur.
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, ASP.NET Core gelen istekten giden HTTP İstemci isteklerine HTTP üst bilgilerini yayma aracıdır. Üst bilgi yayma kullanmak için:

  • Microsoft.AspNetCore.HeaderPropagation paketine bakın.

  • ara yazılımı ve 'de HttpClient Startup yapılandırma:

    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

Göre: Gordon Larkin, Steve Gordon, Gordonn Condronve Ryan Nowak.

, IHttpClientFactory bir uygulamada örnekleri yapılandırmak ve oluşturmak için HttpClient kaydedile ve kullanılabilir. IHttpClientFactory aşağıdaki avantajları sunar:

  • Mantıksal örnekleri adlandırmak ve yapılandırmak için merkezi bir HttpClient konum sağlar. Örneğin, github adlı bir istemci kaydedilene ve GitHub. Genel erişim için varsayılan bir istemci kayded olabilir.
  • içinde işleyiciler için delete aracılığıyla giden ara yazılım kavramını HttpClient kodlar. içinde işleyicileri seçme avantajını elde etmek için Polly tabanlı ara yazılım için uzantılar HttpClient sağlar.
  • Temel alınan örneklerin havuzlarını ve yaşam HttpClientMessageHandler sürelerini yönetir. Otomatik yönetim, yaşam sürelerini el ile yönetme sırasında oluşan yaygın DNS (Etki Alanı Adı Sistemi) HttpClient sorunlarını önler.
  • Fabrika tarafından oluşturulan istemciler aracılığıyla gönderilen tüm ILogger istekler için yapılandırılabilir bir günlük deneyimi (aracılığıyla) ekler.

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

Bu konu sürümündeki örnek kod, System.Text.Json HTTP yanıtlarında döndürülen JSON içeriğininserializesini almak için kullanır. ve kullanan örnekler Json.NET için ReadAsAsync<T> sürüm seçiciyi kullanarak bu konunun 2.x sürümünü seçin.

Tüketim desenleri

Bir uygulamada çeşitli IHttpClientFactory yollarla kullanılabilir:

En iyi yaklaşım, uygulamanın gereksinimlerine bağlıdır.

Temel kullanım

IHttpClientFactory çağrısıyla kaydedil AddHttpClient olabilir:

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.

IHttpClientFactory, bağımlılık ekleme (DI) kullanılarak talep edilebilir. Aşağıdaki kod bir IHttpClientFactory örnek oluşturmak için HttpClient kullanır:

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

Yukarıdaki IHttpClientFactory örnekte olduğu gibi kullanmak, mevcut bir uygulamayı yeniden düzenlemenin iyi bir yolu olabilir. Bu, nasıl kullanılır? üzerinde HttpClient hiçbir etkisi yoktur. Örneklerin HttpClient var olan bir uygulamada oluşturulacak olduğu yerlerde, bu oluşumları çağrısıyla CreateClient değiştirin.

Adlandırılmış istemciler

Adlandırılmış istemciler şu zaman iyi bir seçimdir:

  • Uygulamanın birçok farklı kullanımı HttpClient gerekir.
  • Çoğunda HttpClient farklı yapılandırmalar vardır.

adlı bir için HttpClient yapılandırma, içinde kayıt sırasında belirtilebilir: Startup.ConfigureServices

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");
});

Önceki kodda istemci aşağıdakilerle yapılandırılmıştır:

  • Temel https://api.github.com/ adres.
  • GitHub API'si ile çalışmak için GitHub gerekir.

CreateClient

Her zaman CreateClient çağrılır:

  • yeni bir HttpClient örneği oluşturulur.
  • Yapılandırma eylemi çağrılır.

Adlandırılmış bir istemci oluşturmak için adını içine CreateClient girin:

public class NamedClientModel : PageModel
{
    private readonly IHttpClientFactory _clientFactory;

    public IEnumerable<GitHubPullRequest> PullRequests { get; private set; }

    public bool GetPullRequestsError { get; private set; }

    public bool HasPullRequests => PullRequests.Any();

    public NamedClientModel(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }

    public async Task OnGet()
    {
        var request = new HttpRequestMessage(HttpMethod.Get,
            "repos/dotnet/AspNetCore.Docs/pulls");

        var client = _clientFactory.CreateClient("github");

        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            using var responseStream = await response.Content.ReadAsStreamAsync();
            PullRequests = await JsonSerializer.DeserializeAsync
                    <IEnumerable<GitHubPullRequest>>(responseStream);
        }
        else
        {
            GetPullRequestsError = true;
            PullRequests = Array.Empty<GitHubPullRequest>();
        }
    }
}

Yukarıdaki kodda isteğin bir konak adı belirtmesi gerekm yok. İstemci için yapılandırılan temel adres kullanıldığından kod yalnızca yolu geçebilirsiniz.

Türe bağlı istemciler

Türe bağlı istemciler:

  • Dizeleri anahtar olarak kullanmaya gerek kalmadan adlandırılmış istemcilerle aynı özellikleri sağlar.
  • İstemcileri tüketen IntelliSense ve derleyici yardımı sağlar.
  • Yapılandırmak ve belirli bir ile etkileşim kurmak için tek bir konum HttpClient sağlama. Örneğin, türü tek bir istemci kullanılabilir:
    • Tek bir arka uç uç noktası için.
    • Uç noktayla ilgili tüm mantığı kapsülleme.
  • DI ile çalışma ve uygulamaya gerektiğinde ekleme.

Türü türüne sahip bir istemci, HttpClient oluşturucusnda bir 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üne göre istemciye taşınır.
  • nesnesi HttpClient genel bir özellik olarak ortaya çıkar.

İşlevselliği ortaya çıkaran API'ye özgü HttpClient yöntemler oluşturulabilir. Örneğin yöntemi, GetAspNetDocsIssues açık sorunları almak için kodu kapsüller.

Aşağıdaki kod, türü AddHttpClient türü doğru olan bir istemci Startup.ConfigureServices sınıfını kaydetmek için çağrısında dır:

services.AddHttpClient<GitHubService>();

Türe sahip istemci, DI ile geçici olarak kaydedilir. Yukarıdaki kodda, AddHttpClient geçici bir hizmet olarak GitHubService kaydolr. Bu kayıt, aşağıdaki işlemleri yapmak için bir fabrika yöntemi kullanır:

  1. HttpClient örneği oluşturun.
  2. örneğini oluşturucuya GitHubService geçirmesi için bir HttpClient örneği oluşturun.

Türü yazilen istemci doğrudan ekleme ve tüketilebilir:

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şturucusu yerine 'de Startup.ConfigureServices kayıt sırasında belirtilebilir:

services.AddHttpClient<RepoService>(c =>
{
    c.BaseAddress = new Uri("https://api.github.com/");
    c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});

HttpClienttürüne göre bir istemci içinde kapsül olabilir. Bunu bir özellik olarak açığa çıkararak değil, ö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 ortak yöntemine GetRepos göredir.

Oluşturulan istemciler

IHttpClientFactoryYeniden Sığdır gibi üçüncü taraf kitaplıklarla birlikte kullanılabilir. Yeniden sığdır, .NET için bir REST kitaplığıdır. REST API'leri canlı arabirimlere dönüştürür. arabiriminin bir uygulaması, dış HTTP çağrıları yapmak RestService için kullanılarak dinamik olarak HttpClient oluşturulur.

Dış API'yi ve yanıtını temsil etmek için bir arabirim ve yanıt tanımlanır:

public interface IHelloClient
{
    [Get("/helloworld")]
    Task<Reply> GetMessageAsync();
}

public class Reply
{
    public string Message { get; set; }
}

Türü oluşturulan bir istemci, uygulama oluşturmak için Yeniden Sığdır kullanılarak 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 gerektiğinde DI ve Yeniden Sığdır tarafından sağlanan uygulamayla birlikte 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 aşağıdakiler dahil olmak üzere diğer HTTP fiillerini de destekler:

  • POST
  • PUT
  • DELETE
  • YAMA

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

Aşağıdaki örnekte, bir HTTP POST isteğinin nasıl gerçekleştirl olduğu gösterir:

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 ayrıca diğer içerik türlerini de destekler. Örneğin MultipartContent ve StreamContent. Desteklenen içeriğin tam listesi için bkz. HttpContent .

Aşağıdaki örnekte bir HTTP PUT isteği gösterir:

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. yöntemi SaveItemAsync yerine PutAsync yöntemini PostAsync çağıran.

Aşağıdaki örnekte bir HTTP DELETE isteği gösterir:

public async Task DeleteItemAsync(long itemId)
{
    using var httpResponse =
        await _httpClient.DeleteAsync($"/api/TodoItems/{itemId}");

    httpResponse.EnsureSuccessStatusCode();
}

Yukarıdaki kodda yöntemi DeleteItemAsync çağrısında DeleteAsync dır. HTTP DELETE istekleri genellikle gövde içermeyer, yöntemi bir örneğini kabul eden DeleteAsync bir aşırı yükleme HttpContent sağlamaz.

ile farklı HTTP fiilleri kullanma hakkında daha fazla bilgi edinmek HttpClient için bkz. HttpClient .

Giden istek ara yazılımı

HttpClient , giden HTTP istekleri için birbirine bağlanacak işleyiciler için delegelik kavramına sahip. IHttpClientFactory:

  • Adlandırılmış her istemciye uygulanacak işleyicileri tanımlamayı basitler.

  • Giden istek ara yazılım işlem hattı oluşturmak için birden çok işleyicinin kaydını ve zincirlesini destekler. Bu işleyicilerin her biri, giden istekten önce ve sonra iş gerçekleştire bir işleyicidir. Bu düzen:

    • , ağ trafiğinde gelen ara yazılım işlem hattına ASP.NET Core.
    • HTTP istekleriyle ilgili çapraz kesme endişelerini yönetmek için bir mekanizma sağlar, örneğin:
      • Önbelleğe alma
      • hata işleme
      • Seri -leştirme
      • günlüğe kaydetme

Bir işleyiciyi tapan oluşturmak için:

  • 'den DelegatingHandler türetin.
  • geçersiz SendAsync kılın. İsteği işlem hattındaki sonraki işleyiciye geçirmeden önce kodu yürütü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, üst X-API-KEY bilginin istekte olup olduğunu denetler. Eksikse X-API-KEY BadRequest döndürülür.

ile yapılandırmasına birden fazla işleyici HttpClient Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandler 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 kaydedilmiştir. Kaydedildiktan AddHttpMessageHandler sonra, işleyicinin türünü geçerek çağrıl olabilir.

Birden çok işleyici, yürütmeleri gereken sırayla kayded olabilir. Son işleyici isteği yürütene kadar her işleyici HttpClientHandler 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

Yeni IHttpClientFactory bir delegating 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 kapsamlı bir hizmet tükettiği zaman şaşırtıcı davranışlara yol açabilirsiniz.

Örneğin, tanımlayıcıya sahip bir işlem olarak bir görevi temsil eden aşağıdaki arabirimini ve uygulamasını göz önünde OperationId bulundurabilirsiniz:

public interface IOperationScoped 
{
    string OperationId { get; }
}

public class OperationScoped : IOperationScoped
{
    public string OperationId { get; } = Guid.NewGuid().ToString()[^4..];
}

Adı da anlaşılacağı IOperationScoped gibi, kapsamlı bir yaşam süresi kullanılarak DI'ye kaydedilir:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<TodoContext>(options =>
        options.UseInMemoryDatabase("TodoItems"));

    services.AddHttpContextAccessor();

    services.AddHttpClient<TodoClient>((sp, httpClient) =>
    {
        var httpRequest = sp.GetRequiredService<IHttpContextAccessor>().HttpContext.Request;

        // For sample purposes, assume TodoClient is used in the context of an incoming request.
        httpClient.BaseAddress = new Uri(UriHelper.BuildAbsolute(httpRequest.Scheme,
                                         httpRequest.Host, httpRequest.PathBase));
        httpClient.Timeout = TimeSpan.FromSeconds(5);
    });

    services.AddScoped<IOperationScoped, OperationScoped>();
    
    services.AddTransient<OperationHandler>();
    services.AddTransient<OperationResponseHandler>();

    services.AddHttpClient("Operation")
        .AddHttpMessageHandler<OperationHandler>()
        .AddHttpMessageHandler<OperationResponseHandler>()
        .SetHandlerLifetime(TimeSpan.FromSeconds(5));

    services.AddControllers();
    services.AddRazorPages();
}

Aşağıdaki delegating işleyicisi, giden isteğin IOperationScoped üst bilgisini X-OPERATION-ID ayarlamak için kullanır:

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 [ HttpRequestsSample ] sayfasında](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/fundamentals/http-requests/samples/3.x/HttpRequestsSample)sayfasına gidin /Operation ve sayfayı yenileyin. İstek kapsamı değeri her istek için değişir, ancak işleyici kapsamı değeri yalnızca 5 saniyede bir değişir.

İşleyiciler herhangi bir kapsamda hizmetlere bağımlı olabilir. İşleyicinin bağımlı olduğu hizmetler, işleyici atıldıkları zaman atıldı.

İ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, Polly üçüncü taraf kitaplığıyla tümleştirildi. Polly, .NET için kapsamlı bir resilians ve geçici hata işleme kitaplığıdır. Geliştiricilerin Retry, Circuit Breaker, Timeout, Bulkhead Isolation ve Fallback gibi ilkeleri akıcı ve iş parçacığı güvenli bir şekilde ifade etmelerini sağlar.

Uzantı yöntemleri, yapılandırılmış örneklerle Polly ilkelerinin kullanımını etkinleştirmek için HttpClient sağlanır. Polly uzantıları, istemcilere Polly tabanlı işleyiciler eklemeyi destekler. Polly, Microsoft.Extensions.Http.Polly NuGet gerektirir.

Geçici hataları işleme

Hatalar genellikle dış HTTP çağrıları geçici olduğunda oluşur. AddTransientHttpErrorPolicy , geçici hataları işlemek için bir ilkenin tanımlanmalıdır. ile yapılandırılan AddTransientHttpErrorPolicy ilkeler aşağıdaki yanıtları işlemektedir:

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.

Yukarıdaki kodda bir WaitAndRetryAsync ilke tanımlanmıştır. Başarısız istekler denemeler arasında 600 ms gecikmeyle üç kez yeniden denendi.

İlkeleri dinamik olarak seçme

Uzantı yöntemleri, Polly tabanlı işleyiciler eklemek için sağlanır, örneğin, AddPolicyHandler . Aşağıdaki aşırı AddPolicyHandler yükleme, hangi ilkenin uygulanacak olduğuna karar verme isteğini 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 bir zaman aşımı kullanılır.

Birden çok Polly işleyicisi ekleme

Polly ilkelerini iç içe yerleştirme yaygındı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 bir AddTransientHttpErrorPolicy yeniden deneme ilkesi eklemek için kullanır. Başarısız istekler en fazla üç kez yeniden denendi.
  • İkinci çağrı AddTransientHttpErrorPolicy bir devre kesici ilkesi ekler. 5 başarısız deneme sırayla gerçekleşirse 30 saniye daha dış istek engellenir. Devre kesici ilkeleri durum bilgilidir. 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 ile PolicyRegistry 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 IHttpClientFactory Polly tümleştirmeleri hakkında daha fazla bilgi için bkz. Polly wiki.

HttpClient ve yaşam süresi yönetimi

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

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

Her işleyici genellikle kendi temel HTTP bağlantılarını yönetirken işleyicilerin havuzu tercih edilir. Gerekenden daha fazla işleyici oluşturmak bağlantı gecikmeleri ile sonuçlandırabilirsiniz. Bazı işleyiciler ayrıca bağlantıları süresiz olarak açık tutmakta ve bu da işleyicinin DNS (Etki Alanı Adı Sistemi) değişikliklerine tepki vermesini önlemektedir.

Varsayılan işleyicinin ömrü iki dakikadır. Varsayılan değer, adlandırılmış istemci başına 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ı gerekmeden .NET nesneleri olarak kabul edilebilir. Atma, giden istekleri iptal eder ve HttpClient çağrıldikten sonra verilen örneğin kullanılamaz durumda olacağını garanti Dispose eder. IHttpClientFactory örnekler tarafından kullanılan kaynakları izler ve HttpClient atlar.

Tek bir örneği HttpClient uzun süre canlı tutmak, kullanılmaya başlamadan önce yaygın olarak kullanılan bir desendir. IHttpClientFactory bu düzen, 'a katıldıktan sonra gereksiz hale IHttpClientFactory gelir.

IHttpClientFactory'nin alternatifleri

IHttpClientFactoryDI özellikli bir uygulamada kullanmak şunları önler:

  • Örnekleri havuza alan kaynak tükenme HttpMessageHandler sorunları.
  • Düzenli aralıklarla örneklerin atarak eski DNS HttpMessageHandler sorunları.

Uzun süreli bir örneği kullanarak önceki sorunları çözmenin alternatif yolları SocketsHttpHandler vardır.

  • Uygulama başlatıldığında SocketsHttpHandler bir örneği oluşturun ve uygulamanın ömrü boyunca bunu kullanın.
  • DNS PooledConnectionLifetime yenileme zamanlarına göre uygun bir değere yapılandırma.
  • Gerektiğinde HttpClient kullanarak new HttpClient(handler, disposeHandler: false) örnekler oluşturun.

Yukarıdaki yaklaşımlar, kaynak yönetimi sorunlarını da IHttpClientFactory benzer şekilde çözer.

  • örnekler SocketsHttpHandler arasında bağlantıları HttpClient paylaşıyor. Bu paylaşım yuva tükenmesini önler.
  • Eski SocketsHttpHandler DNS sorunlarını önlemek için bağlantıları uygun şekilde PooledConnectionLifetime döngüye alar.

CookieS

Havuza alan HttpMessageHandler örnekler, nesnelerin CookieContainer paylaşılır olmasıyla sonuç verir. Beklentisiz nesne paylaşımı CookieContainer genellikle yanlış koda neden olur. gerektiren uygulamalar için cookie şu iki işlemden birini değerlendirin:

  • Otomatik işlemeyi devre cookie dışı bırakma
  • Kaçın -arak IHttpClientFactory

Otomatik ConfigurePrimaryHttpMessageHandler işlemeyi devre dışı bırakmak için cookie çağrısı:

services.AddHttpClient("configured-disable-automatic-cookies")
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        return new HttpClientHandler()
        {
            UseCookies = false,
        };
    });

Günlüğe Kaydetme

Tüm istekler IHttpClientFactory için kayıt günlüğü iletileriyle 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 bilgilerini günlüğe kaydetme gibi ek günlükler yalnızca izleme düzeyinde dahil edilir.

Her istemci için kullanılan günlük kategorisi istemcinin adını içerir. Örneğin MyNamedClient adlı bir istemci iletileri "System.Net.Http.HttpClient" kategorisiyle günlüğe kaydeder. MyNamedClient. LogicalHandler". LogicalHandler ile sonekli iletiler, istek işleyicisi işlem hattının dışında oluşur. İstekte iletiler, işlem hattında 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, istek işleyicisi işlem hattında da gerçekleşir. MyNamedClient örneğinde, bu iletiler "System.Net.Http.HttpClient" günlük kategorisiyle günlüğe kaydedilir. MyNamedClient. ClientHandler". İstek için, bu durum diğer tüm işleyiciler çalıştırktan ve istek gönderilmeden hemen önce gerçekleşir. Yanıtta bu günlük, işleyici işlem hattına geri dönmeden ö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 incesini sağlar. Bu, istek üst bilgisinde veya yanıt durum kodunda yapılan değişiklikleri içerebilir.

İstemcinin adını günlük kategorisine dahil olmak, belirli adlandırılmış istemciler için günlük filtrelemeyi sağlar.

HttpMessageHandler'ı yapılandırma

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

Adlandırılmış IHttpClientBuilder veya türe bağlı istemciler ekliyken bir döndürülür. Uzantı ConfigurePrimaryHttpMessageHandler 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 aşağıdaki paket başvurularını projeye ekleyin:

Aşağıdaki örnekte:

  • IHttpClientFactory , Genel Ana Bilgisayar'ın hizmet kapsayıcısı içinde kayıtlıdır.
  • MyService , hizmetten bir oluşturmak için kullanılan bir istemci fabrika örneği HttpClient oluşturur. HttpClient bir web sayfasını almak için kullanılır.
  • Main hizmetin yöntemini yürütmek ve web sayfası içeriğinin ilk GetPage 500 karakteri konsola yazmak için bir kapsam oluşturur.
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 istemci isteklerine HTTP üstbilgilerini yaymaya yönelik bir ASP.NET Core ara istemcindedir. Üst bilgi yaymayı kullanmak için:

  • Microsoft. AspNetCore. Headeryayma paketine başvurun.

  • Ara yazılımı ve HttpClient içinde yapılandırın Startup :

    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ılan üst bilgileri içerir:

    var client = clientFactory.CreateClient("MyForwardingClient");
    var response = client.GetAsync(...);
    

Ek kaynaklar

Kirk Larkabağı, Steve Gordon, Glenn CONDRONve Ryan şimdi ak.

Bir IHttpClientFactory uygulamadaki örnekleri yapılandırmak ve oluşturmak için kayıt yapılabilir ve kullanılabilir HttpClient . IHttpClientFactory aşağıdaki avantajları sunar:

  • , Mantıksal örnekleri adlandırmak ve yapılandırmak için merkezi bir konum sağlar HttpClient . Örneğin, GitHub adlı bir istemci, GitHuberişmek için kaydedilebilir ve yapılandırılabilir. Varsayılan istemci, genel erişim için kaydedilebilir.
  • ' De işleyiciler temsilci seçme yoluyla giden ara yazılım kavramını daha da artırır HttpClient . ' De işleyiciler temsilci atama avantajlarından faydalanmak için, Polya tabanlı bir ara yazılım için uzantılar sağlar HttpClient .
  • Temel örneklerin biriktirmesini ve ömrünü yönetir HttpClientMessageHandler . Otomatik yönetim, yaşam sürelerini el ile yönetirken oluşan ortak DNS (etki alanı adı sistemi) sorunlarını önler HttpClient .
  • ILoggerFabrika tarafından oluşturulan istemciler aracılığıyla gönderilen tüm istekler için yapılandırılabilir bir günlüğe kaydetme deneyimi ekler (aracılığıyla).

Örnek kodu görüntüleyin veya indirin (nasıl indirilir).

Bu konu sürümündeki örnek kod, System.Text.Json http yanıtlarında döndürülen JSON içeriğinin serisini kaldırmak için kullanır. Ve kullanan örnekler için Json.NET ReadAsAsync<T> , bu konunun 2. x sürümünü seçmek üzere sürüm seçiciyi kullanın.

Tüketim desenleri

Bir uygulamada çeşitli yollar IHttpClientFactory kullanılabilir:

En iyi yaklaşım, uygulamanın gereksinimlerine bağlı olarak değişir.

Temel kullanım

IHttpClientFactory , çağırarak kaydedilebilir AddHttpClient :

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.

IHttpClientFactory Bağımlılık ekleme (dı)kullanılarak bir istek yapılabilir. Aşağıdaki kod IHttpClientFactory bir örnek oluşturmak için kullanır HttpClient :

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

IHttpClientFactoryYukarıdaki örnekte olduğu gibi kullanmak, mevcut bir uygulamayı yeniden düzenleme için iyi bir yoldur. Kullanım hakkında hiçbir etkisi yoktur HttpClient . HttpClientVar olan bir uygulamada örneklerin oluşturulduğu yerlerde, bu oluşumların ' i çağrılarıyla değiştirin CreateClient .

Adlandırılmış istemciler

Adlandırılmış istemciler şu durumlarda iyi bir seçimdir:

  • Uygulama birçok farklı kullanımı gerektirir HttpClient .
  • Birçok HttpClient s farklı yapılandırmaya sahiptir.

Adlandırılmış için yapılandırma HttpClient , içinde kayıt sırasında belirtilebilir Startup.ConfigureServices :

services.AddHttpClient("github", c =>
{
    c.BaseAddress = new Uri("https://api.github.com/");
    // Github API versioning
    c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    // Github requires a user-agent
    c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});

İstemcinin yapılandırıldığı önceki kodda:

  • Temel adres https://api.github.com/ .
  • GitHub apı ile çalışmak için iki üst bilgi gereklidir.

CreateClient

Her zaman CreateClient çağrılır:

  • Yeni bir örneği HttpClient oluşturulur.
  • Yapılandırma eylemi çağrılır.

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

public class NamedClientModel : PageModel
{
    private readonly IHttpClientFactory _clientFactory;

    public IEnumerable<GitHubPullRequest> PullRequests { get; private set; }

    public bool GetPullRequestsError { get; private set; }

    public bool HasPullRequests => PullRequests.Any();

    public NamedClientModel(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }

    public async Task OnGet()
    {
        var request = new HttpRequestMessage(HttpMethod.Get,
            "repos/dotnet/AspNetCore.Docs/pulls");

        var client = _clientFactory.CreateClient("github");

        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            using var responseStream = await response.Content.ReadAsStreamAsync();
            PullRequests = await JsonSerializer.DeserializeAsync
                    <IEnumerable<GitHubPullRequest>>(responseStream);
        }
        else
        {
            GetPullRequestsError = true;
            PullRequests = Array.Empty<GitHubPullRequest>();
        }
    }
}

Yukarıdaki kodda, isteğin bir ana bilgisayar adı belirtmesi gerekmez. İstemci için yapılandırılan taban adresi kullanıldığından, kod yalnızca yolu geçirebilir.

Yazılan istemciler

Yazılan istemciler:

  • Dizeleri anahtar olarak kullanma gereksinimi olmadan, adlandırılmış istemcilerle aynı özellikleri sağlayın.
  • İstemcileri tükettiren IntelliSense ve derleyici yardımı sağlar.
  • Yapılandırmak ve belirli bir ile etkileşimde bulunmak için tek bir konum belirtin HttpClient . Örneğin, tek bir türü belirtilmiş istemci kullanılabilir:
    • Tek bir arka uç uç noktası için.
    • Uç nokta ile ilgili tüm mantığı kapsüllemek için.
  • DI ile birlikte çalışın ve uygulamada gerektiğinde eklenebilir.

Türü belirtilmiş istemci HttpClient , oluşturucusunda bir parametreyi kabul eder:

public class GitHubService
{
    public HttpClient Client { get; }

    public GitHubService(HttpClient client)
    {
        client.BaseAddress = new Uri("https://api.github.com/");
        // GitHub API versioning
        client.DefaultRequestHeaders.Add("Accept",
            "application/vnd.github.v3+json");
        // GitHub requires a user-agent
        client.DefaultRequestHeaders.Add("User-Agent",
            "HttpClientFactory-Sample");

        Client = client;
    }

    public async Task<IEnumerable<GitHubIssue>> GetAspNetDocsIssues()
    {
        var response = await Client.GetAsync(
            "/repos/dotnet/AspNetCore.Docs/issues?state=open&sort=created&direction=desc");

        response.EnsureSuccessStatusCode();

        using var responseStream = await response.Content.ReadAsStreamAsync();
        return await JsonSerializer.DeserializeAsync
            <IEnumerable<GitHubIssue>>(responseStream);
    }
}

Ingilizce dışındaki dillere çevrilmiş kod açıklamalarını görmek isterseniz, Bu GitHub tartışma sorununubize tanıyın.

Yukarıdaki kodda:

  • Yapılandırma, yazılan istemciye taşınır.
  • HttpClientNesne bir ortak özellik olarak sunulur.

İşlevselliği kullanıma sunan, API 'ye özgü Yöntemler oluşturulabilir HttpClient . Örneğin, GetAspNetDocsIssues yöntemi açık sorunları almak için kodu kapsüller.

Aşağıdaki kod, AddHttpClient Startup.ConfigureServices türü belirtilmiş bir istemci sınıfını kaydetmek için ' de çağırır:

services.AddHttpClient<GitHubService>();

Yazılan istemci, DI ile geçici olarak kaydedilir. Yukarıdaki kodda AddHttpClient GitHubService geçici bir hizmet olarak kaydedilir. Bu kayıt, için bir fabrika yöntemi kullanır:

  1. HttpClient örneği oluşturun.
  2. Örneğini oluşturucusuna geçirerek bir örneğini oluşturun GitHubService HttpClient .

Yazılan istemci doğrudan eklenebilir ve tüketilebilir:

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ı Startup.ConfigureServices , türü belirlenmiş istemcinin Oluşturucusu yerine kayıt sırasında belirtilebilir:

services.AddHttpClient<RepoService>(c =>
{
    c.BaseAddress = new Uri("https://api.github.com/");
    c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});

, HttpClient Türü belirlenmiş bir istemci içinde kapsüllenebilir. Bunu bir özellik olarak göstermek yerine, örneği dahili olarak çağıran bir yöntem tanımlayın HttpClient :

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 bir özel alanda depolanır. Öğesine erişimi, HttpClient genel yöntemi tarafından yapılır GetRepos .

Oluşturulan istemciler

IHttpClientFactory , yeniden sığdırmagibi üçüncü taraf kitaplıklarla birlikte kullanılabilir. Yeniden sığdırma, .NET için bir REST kitaplığıdır. REST API 'Leri canlı arabirimlere dönüştürür. Arabirim bir uygulama, RestService HttpClient dış http çağrıları yapmak için kullanılarak tarafından dinamik olarak oluşturulur.

Bir arabirim ve yanıt, dış API 'yi ve yanıtını temsil edecek şekilde tanımlanır:

public interface IHelloClient
{
    [Get("/helloworld")]
    Task<Reply> GetMessageAsync();
}

public class Reply
{
    public string Message { get; set; }
}

Türü belirlenmiş bir istemci eklenebilir, uygulamayı oluşturmak için yeniden sığdırma kullanımı kullanılabilir:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient("hello", c =>
    {
        c.BaseAddress = new Uri("http://localhost:5000");
    })
    .AddTypedClient(c => Refit.RestService.For<IHelloClient>(c));

    services.AddControllers();
}

Tanımlı arabirim, gereken yerde, mak ve Refit tarafından sağlanmış uygulama ile 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();
    }
}

GÖNDERI, PUT ve DELETE isteklerini oluşturma

Yukarıdaki örneklerde, tüm HTTP istekleri GET HTTP fiilini kullanır. HttpClient , aşağıdakiler de dahil olmak üzere diğer HTTP fiillerini de destekler:

  • POST
  • PUT
  • DELETE
  • DÜZELTMESI

Desteklenen HTTP fiillerinin tüm listesi için bkz HttpMethod ..

Aşağıdaki örnek, bir HTTP POST isteğinin nasıl yapılacağını göstermektedir:

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:

  • TodoItemParametresini kullanarak JSON için parametreyi seri hale getirir System.Text.Json . Bu JsonSerializerOptions , serileştirme işlemini yapılandırmak için bir örneğini kullanır.
  • StringContentHttp isteğinin gövdesinde göndermek üzere seri hale GETIRILMIŞ JSON paketini paketlemek için bir örneği oluşturur.
  • PostAsyncJSON içeriğini BELIRTILEN URL 'ye göndermek için çağrılar. Bu, HttpClient. BaseAddress'e eklenen GÖRELI bir URL 'dir.
  • EnsureSuccessStatusCodeYanıt durum kodu başarıyı belirtmezse bir özel durum oluşturmak için çağırır.

HttpClient , diğer içerik türlerini de destekler. Örneğin MultipartContent ve StreamContent. Desteklenen içeriğin tamamı 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. SaveItemAsyncYöntemi yerine çağırır PutAsync PostAsync .

Aşağıdaki örnekte bir HTTP SILME isteği gösterilmektedir:

public async Task DeleteItemAsync(long itemId)
{
    using var httpResponse =
        await _httpClient.DeleteAsync($"/api/TodoItems/{itemId}");

    httpResponse.EnsureSuccessStatusCode();
}

Yukarıdaki kodda, DeleteItemAsync yöntemi çağırır DeleteAsync . HTTP DELETE istekleri genellikle gövde içermediğinden, DeleteAsync yöntemi bir örneğini kabul eden bir aşırı yükleme sağlamaz HttpContent .

İle farklı HTTP fiillerini kullanma hakkında daha fazla bilgi için HttpClient bkz HttpClient ..

Giden istek ara yazılımı

HttpClient , giden HTTP istekleri için birlikte bağlanabilen işleyicileri temsilci seçme kavramıdır. IHttpClientFactory:

  • Her bir adlandırılmış istemci için uygulanacak işleyiciler tanımlamayı basitleştirir.

  • Bir giden istek ara yazılım işlem hattı oluşturmak için birden çok işleyicinin kaydedilmesini ve zincirleme kullanımını destekler. Bu işleyicilerin her biri, giden istekten önce ve sonra iş gerçekleştirebilir. Bu model:

    • ASP.NET Core gelen ara yazılım ardışık düzenine benzerdir.
    • , HTTP istekleri etrafında çapraz kesme sorunlarını yönetmek için bir mekanizma sağlar, örneğin:
      • önbelleği
      • hata işleme
      • getir
      • günlüğe kaydetme

Temsilci seçme işleyicisi oluşturmak için:

  • Türet DelegatingHandler .
  • Geçersiz kıl SendAsync . İsteği ardışık düzen içindeki bir sonraki işleyiciye geçirmeden önce kodu yürütü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, üstbilginin istekte olup olmadığını denetler X-API-KEY . X-API-KEYEksikse, BadRequest döndürülür.

İçin yapılandırmasına birden fazla işleyici eklenebilir HttpClient Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandler :

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 dı ile kaydedilir. Kaydedildikten sonra, AddHttpMessageHandler işleyicinin türü geçirerek, çağrılabilir.

Birden çok işleyici, yürütülmesi gereken sırayla kaydedilebilir. Her işleyici, son isteği çalıştırana kadar sonraki işleyiciyi sarmalar HttpClientHandler :

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 ortamında DI kullanma

IHttpClientFactoryYeni bir temsilci seçme işleyicisi oluşturduğunda, işleyicinin Oluşturucu parametrelerini karşılamak IÇIN dı kullanır. IHttpClientFactory Her işleyici için ayrı bir dı kapsamı oluşturur ve bu, bir işleyici kapsamlı bir hizmeti tükettiği zaman ortaya çıkmasına neden olabilir.

Örneğin, bir görevi tanımlayıcı ile bir işlem olarak temsil eden aşağıdaki arabirimi ve uygulamasını göz önünde bulundurun OperationId :

public interface IOperationScoped 
{
    string OperationId { get; }
}

public class OperationScoped : IOperationScoped
{
    public string OperationId { get; } = Guid.NewGuid().ToString()[^4..];
}

Adından da anlaşılacağı gibi, IOperationScoped kapsamlı bir yaşam süresi kullanılarak dı ile kaydedilir:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<TodoContext>(options =>
        options.UseInMemoryDatabase("TodoItems"));

    services.AddHttpContextAccessor();

    services.AddHttpClient<TodoClient>((sp, httpClient) =>
    {
        var httpRequest = sp.GetRequiredService<IHttpContextAccessor>().HttpContext.Request;

        // For sample purposes, assume TodoClient is used in the context of an incoming request.
        httpClient.BaseAddress = new Uri(UriHelper.BuildAbsolute(httpRequest.Scheme,
                                         httpRequest.Host, httpRequest.PathBase));
        httpClient.Timeout = TimeSpan.FromSeconds(5);
    });

    services.AddScoped<IOperationScoped, OperationScoped>();
    
    services.AddTransient<OperationHandler>();
    services.AddTransient<OperationResponseHandler>();

    services.AddHttpClient("Operation")
        .AddHttpMessageHandler<OperationHandler>()
        .AddHttpMessageHandler<OperationResponseHandler>()
        .SetHandlerLifetime(TimeSpan.FromSeconds(5));

    services.AddControllers();
    services.AddRazorPages();
}

Aşağıdaki temsilci işleyicisi, IOperationScoped X-OPERATION-ID giden istek için üst bilgiyi ayarlamak için kullanır ve kullanır:

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

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

İşleyiciler herhangi bir kapsamın hizmetlerine bağlı olabilir. İşleyicilerin bağımlı olduğu hizmetler, işleyicinin elden çıkarılmasıyla kaldırılır.

İleti işleyicileriyle istek başına durumu paylaşmak için aşağıdaki yaklaşımlardan birini kullanın:

Polly tabanlı işleyiciler kullanın

IHttpClientFactory üçüncü taraf kitaplığı Pollyile tümleşir. Polly, .NET için kapsamlı bir esnekliği ve geçici hata işleme kitaplığıdır. Geliştiricilerin yeniden deneme, devre kesici, zaman aşımı, Bulkbaş yalıtımı, akıcı ve iş parçacığı açısından güvenli bir şekilde geri dönüş gibi ilkeler almasına olanak tanır.

Uzantı yöntemleri, yapılandırılmış örneklerle Polly ilkelerin kullanımını etkinleştirmek için sağlanır HttpClient . Polly uzantıları, istemcilere Polly tabanlı işleyiciler eklemeyi destekler. polly, Microsoft. Extensions. Http. polly NuGet paketini gerektirir.

Geçici hataları işle

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. AddTransientHttpErrorPolicyAşağıdaki yanıtları işleyecek şekilde yapılandırılan ilkeler:

AddTransientHttpErrorPolicy``PolicyBuilderolası bir 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.

Yukarıdaki kodda bir WaitAndRetryAsync ilke tanımlanmıştır. Başarısız istekler, denemeler arasındaki 600 MS gecikmeyle en fazla üç kez yeniden denenir.

Dinamik olarak ilke seçme

Uzantı yöntemleri, örneğin, Polly tabanlı işleyiciler eklemek için sağlanır 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 HTTP yöntemleri için, 30 saniyelik bir zaman aşımı kullanılır.

Birden çok Polly işleyici ekleme

Polly ilkeleri iç içe almak yaygın bir şekilde yapı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, AddTransientHttpErrorPolicy yeniden deneme ilkesi eklemek için kullanır. Başarısız istekler en fazla üç kez yeniden denenir.
  • İkinci AddTransientHttpErrorPolicy çağrı bir devre kesici ilkesi ekler. 5 başarısız girişim sıralı olarak gerçekleşirse, daha fazla dış istek 30 saniye boyunca engellenir. Devre kesici ilkeleri durum bilgisi vardır. Bu istemci aracılığıyla yapılan tüm çağrılar aynı devre durumunu paylaşır.

Polly kayıt defterinden ilke ekleme

Düzenli olarak kullanılan ilkeleri yönetmeye yönelik bir yaklaşım, bunları bir kez tanımlayıp bir ile kaydetmektir PolicyRegistry .

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 daha fazla tümleştirme hakkında daha fazla bilgi için IHttpClientFactory bkz. Polly wiki.

HttpClient ve ömür yönetimi

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

IHttpClientFactory``HttpMessageHandlerkaynak tüketimini azaltmak için fabrika tarafından oluşturulan örnekleri havuzlar. Bir HttpMessageHandler örnek, süresi dolmamışsa yeni bir örnek oluştururken havuzdan yeniden kullanılabilir HttpClient .

Her işleyici genellikle kendi temel HTTP bağlantılarını yönettiğinden, işleyicilerin havuzlaması tercih edilir. Gerekenden daha fazla işleyici oluşturulması bağlantı gecikmeleri oluşmasına neden olabilir. Ayrıca, bazı işleyiciler bağlantıları süresiz olarak açık tutar, bu da işleyicinin DNS (etki alanı adı sistemi) değişikliklerine yeniden davranmasını engelleyebilir.

Varsayılan işleyici ömrü iki dakikadır. Varsayılan değer, adlandırılmış istemci temelinde geçersiz kılınabilir:

public void ConfigureServices(IServiceCollection services)
{           
    services.AddHttpClient("extendedhandlerlifetime")
        .SetHandlerLifetime(TimeSpan.FromMinutes(5));

    // Remaining code deleted for brevity.

HttpClient örnekler genellikle aktiften çıkarma gerektirmeyen .NET nesneleri olarak kabul edilebilir. Çıkarma giden istekleri iptal eder ve HttpClient çağırma sonrasında verilen örneğin kullanılamaz olmasını sağlar Dispose . IHttpClientFactory örnekler tarafından kullanılan kaynakları izler ve ortadan kaldırdık HttpClient .

Tek HttpClient bir örneğinin uzun süre canlı tutulması, önünde kullanılmadan önce kullanılan ortak bir modeldir IHttpClientFactory . Bu model, ' a geçtikten sonra gereksiz hale gelir IHttpClientFactory .

Ihttpclientfactory alternatifleri

IHttpClientFactoryDı etkin bir uygulamada kullanmak şunları önler:

  • Havuz örneklerine göre kaynak tükenmesi sorunları HttpMessageHandler .
  • Düzenli aralıklarla örnekleri geçirerek eski DNS sorunları HttpMessageHandler .

Uzun süreli bir örnek kullanarak önceki sorunları çözmenin alternatif yolları vardır SocketsHttpHandler .

  • SocketsHttpHandlerUygulamanın başladığı zaman bir örneği oluşturun ve uygulamanın ömrü boyunca kullanın.
  • PooledConnectionLifetimeDNS yenileme süreleri temelinde uygun bir değere yapılandırın.
  • HttpClientGerektiğinde örnek oluşturun new HttpClient(handler, disposeHandler: false) .

Yukarıdaki yaklaşımlar, IHttpClientFactory benzer bir şekilde çözen kaynak yönetimi sorunlarını çözer.

  • , SocketsHttpHandler Örnekleri arasında bağlantıları paylaşır HttpClient . Bu paylaşım, yuva azalmasına engel olur.
  • SocketsHttpHandlerBağlantıları, PooledConnectionLifetime eski DNS sorunlarından kaçınmak için öğesine göre döngüler.

Cookiemalar

Havuza alınmış HttpMessageHandler örnekler, CookieContainer paylaşılan nesneler ile sonuçlanır. Beklenmeyen CookieContainer nesne paylaşımı genellikle hatalı kodla sonuçlanır. S gerektiren uygulamalar için cookie şunlardan birini göz önünde bulundurun:

  • Otomatik işlemeyi devre dışı bırakma cookie
  • Önlemenin IHttpClientFactory

ConfigurePrimaryHttpMessageHandlerOtomatik işlemeyi devre dışı bırakmak için çağırın cookie :

services.AddHttpClient("configured-disable-automatic-cookies")
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        return new HttpClientHandler()
        {
            UseCookies = false,
        };
    });

Günlüğe Kaydetme

IHttpClientFactoryTüm istekler için kayıt günlüğü iletileri aracılığıyla 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 yer alır.

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 düzeltilen iletiler istek işleyicisi ardışık düzeni dışında oluşur. İstekte, işlem hattındaki diğer işleyiciler işlenmeden önce iletiler günlüğe kaydedilir. Yanıtta, tüm diğer işlem hattı işleyicileri yanıtı aldıktan sonra iletiler günlüğe kaydedilir.

Günlüğe kaydetme, istek işleyicisi ardışık düzeni içinde de gerçekleşir. Mynamedclient örneğinde, bu Iletiler "System .net. http. HttpClient" günlük kategorisiyle günlüğe kaydedilir. Mynamedclient. ClientHandler ". İstek için bu, tüm diğer işleyiciler çalıştırıldıktan sonra ve istek gönderilmeden hemen önce gerçekleşir. Yanıtta, bu günlüğe kaydetme, işleyicinin işleyici işlem hattı üzerinden geri geçirmeden önce yanıtın durumunu içerir.

İşlem hattının dışında ve içinde günlüğe kaydetmenin etkinleştirilmesi, diğer işlem hattı işleyicileri tarafından yapılan değişikliklerin incelemesini etkinleştirir. Bu, istek üst bilgilerinde veya yanıt durum kodunda yapılan değişiklikleri içerebilir.

İstemcinin adını log kategorisinde da içermek, belirli adlandırılmış istemciler için günlük filtrelemeyi sunar.

HttpMessageHandler 'ı yapılandırma

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

IHttpClientBuilderAdlandırılmış veya yazılan istemciler eklenirken bir döndürülür. ConfigurePrimaryHttpMessageHandlerGenişletme yöntemi bir temsilciyi tanımlamak için kullanılabilir. Temsilci, HttpMessageHandler Bu istemci tarafından kullanılan birincili 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 ıhttpclientfactory kullanma

Konsol uygulamasında, aşağıdaki paket başvurularını projeye ekleyin:

Aşağıdaki örnekte:

  • IHttpClientFactory , genel konağın hizmet kapsayıcısına kaydedilir.
  • MyService hizmetinden bir istemci fabrikası örneği oluşturur HttpClient . HttpClient , bir Web sayfasını almak için kullanılır.
  • Main hizmetin yöntemini yürütmek için bir kapsam oluşturur GetPage 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, gelen istekten giden http istemci isteklerine HTTP üstbilgilerini yaymaya yönelik bir ASP.NET Core ara istemcindedir. Üst bilgi yaymayı kullanmak için:

  • Microsoft. AspNetCore. Headeryayma paketine başvurun.

  • Ara yazılımı ve HttpClient içinde yapılandırın Startup :

    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ılan üst bilgileri içerir:

    var client = clientFactory.CreateClient("MyForwardingClient");
    var response = client.GetAsync(...);
    

Ek kaynaklar