Megosztás a következőn keresztül:


HTTP-kérések létrehozása az IHttpClientFactory használatával a ASP.NET Core-ban

Note

Ez nem a cikk legújabb verziója. Az aktuális kiadásról a cikk .NET 10-es verziójában olvashat.

Warning

A ASP.NET Core ezen verziója már nem támogatott. További információt a .NET és a .NET Core támogatási szabályzatában talál. A jelen cikk .NET 9-es verzióját lásd az aktuális kiadásért .

Kirk Larkin, Steve Gordon, Glenn Condron és Ryan Nowak.

Egy IHttpClientFactory regisztrálható és használható HttpClient példányok konfigurálására és létrehozására egy alkalmazásban. IHttpClientFactory a következő előnyöket kínálja:

  • Központi helyet biztosít a logikai HttpClient-példányok elnevezéséhez és konfigurálásához. Például egy github nevű ügyfél regisztrálható és konfigurálható a GitHub eléréséhez. Az alapértelmezett ügyfél regisztrálható az általános hozzáféréshez.
  • Kodifikálja a kimenő köztes szoftver fogalmát a HttpClient delegáló kezelők használatával. Bővítményeket biztosít a Polly-alapú köztes szoftverhez, hogy kihasználhassa a kezelők delegálásának előnyeit a HttpClient.
  • A mögöttes HttpClientMessageHandler példányok készletezését és élettartamát kezeli. Az automatikus felügyelet elkerüli az élettartamok manuális kezelésekor HttpClient előforduló gyakori DNS-(tartománynévrendszer-) problémákat.
  • Konfigurálható naplózási felületet (via ILogger) ad hozzá a gyár által létrehozott ügyfeleken keresztül küldött összes kéréshez.

A jelen témakör verziójának mintakódja a HTTP-válaszokban visszaadott JSON-tartalom deszerializálására szolgál System.Text.Json . Azoknál a mintáknál, amelyek Json.NET és ReadAsAsync<T>-et használnak, a verzióválasztóval válassza ki ennek a témakörnek a 2.x verzióját.

Használati minták

Az alkalmazásokban többféleképpen IHttpClientFactory is használható:

A legjobb módszer az alkalmazás követelményeitől függ.

Alapszintű használat

Regisztráljon IHttpClientFactory, hívja AddHttpClient a Program.cs-ban:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddHttpClient();

Egy IHttpClientFactory kérhető függőséginjektálással (DI). A következő kód IHttpClientFactory-t használ egy HttpClient példány létrehozásához.

public class BasicModel : PageModel
{
    private readonly IHttpClientFactory _httpClientFactory;

    public BasicModel(IHttpClientFactory httpClientFactory) =>
        _httpClientFactory = httpClientFactory;

    public IEnumerable<GitHubBranch>? GitHubBranches { get; set; }

    public async Task OnGet()
    {
        var httpRequestMessage = new HttpRequestMessage(
            HttpMethod.Get,
            "https://api.github.com/repos/dotnet/AspNetCore.Docs/branches")
        {
            Headers =
            {
                { HeaderNames.Accept, "application/vnd.github.v3+json" },
                { HeaderNames.UserAgent, "HttpRequestsSample" }
            }
        };

        var httpClient = _httpClientFactory.CreateClient();
        var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);

        if (httpResponseMessage.IsSuccessStatusCode)
        {
            using var contentStream =
                await httpResponseMessage.Content.ReadAsStreamAsync();
            
            GitHubBranches = await JsonSerializer.DeserializeAsync
                <IEnumerable<GitHubBranch>>(contentStream);
        }
    }
}

Az előző példához hasonló használat IHttpClientFactory jó módszer egy meglévő alkalmazás újrabontására. Nincs hatással a használat módjára HttpClient . Azokon a helyeken, ahol HttpClient példányok jönnek létre egy meglévő alkalmazásban, cserélje le ezeket az előfordulásokat CreateClient hívásokkal.

Névvel ellátott ügyfelek

A nevesített ügyfelek akkor jó választásnak számítanak, ha:

  • Az alkalmazásnak számos különböző módja van HttpClient használatára.
  • Sok HttpClientmás konfigurációval rendelkezik.

Adja meg az elnevezett HttpClient konfigurációját a Program.cs során végzett regisztrációkor:

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

    // using Microsoft.Net.Http.Headers;
    // The GitHub API requires two headers.
    httpClient.DefaultRequestHeaders.Add(
        HeaderNames.Accept, "application/vnd.github.v3+json");
    httpClient.DefaultRequestHeaders.Add(
        HeaderNames.UserAgent, "HttpRequestsSample");
});

Az előző kódban az ügyfél a következőkkel van konfigurálva:

  • Az alapcím https://api.github.com/.
  • A GitHub API használatához két fejléc szükséges.

CreateClient

Minden alkalommal, amikor CreateClient hívásra kerül:

  • Létrejön egy HttpClient új példány.
  • A rendszer meghívja a konfigurációs műveletet.

Névvel ellátott ügyfél létrehozásához adja át az ügyfél nevét a következőbe CreateClient:

public class NamedClientModel : PageModel
{
    private readonly IHttpClientFactory _httpClientFactory;

    public NamedClientModel(IHttpClientFactory httpClientFactory) =>
        _httpClientFactory = httpClientFactory;

    public IEnumerable<GitHubBranch>? GitHubBranches { get; set; }

    public async Task OnGet()
    {
        var httpClient = _httpClientFactory.CreateClient("GitHub");
        var httpResponseMessage = await httpClient.GetAsync(
            "repos/dotnet/AspNetCore.Docs/branches");

        if (httpResponseMessage.IsSuccessStatusCode)
        {
            using var contentStream =
                await httpResponseMessage.Content.ReadAsStreamAsync();
            
            GitHubBranches = await JsonSerializer.DeserializeAsync
                <IEnumerable<GitHubBranch>>(contentStream);
        }
    }
}

Az előző kódban a kérésnek nem kell állomásnevet megadnia. A kód csak az elérési utat tudja átadni, mivel a rendszer az ügyfélhez konfigurált alapcímet használja.

Beírt ügyfelek

Beírt ügyfelek:

  • A névvel ellátott ügyfelekkel azonos képességeket biztosíthat anélkül, hogy sztringeket kellene használnia kulcsként.
  • Az IntelliSense és a fordító segítséget nyújtanak a kliensek használata során.
  • Adjon meg egyetlen helyet egy adott HttpClienthely konfigurálásához és használatához. Például használható egyetlen gépelt ügyfél:
    • Egyetlen háttérvégpont esetén.
    • A végponttal kapcsolatos összes logika beágyazása.
  • Együttműködhet a DI-vel, és szükség esetén injektálható az alkalmazásban.

A gépelt ügyfél egy HttpClient paramétert fogad el a konstruktorában.

public class GitHubService
{
    private readonly HttpClient _httpClient;

    public GitHubService(HttpClient httpClient)
    {
        _httpClient = httpClient;

        _httpClient.BaseAddress = new Uri("https://api.github.com/");

        // using Microsoft.Net.Http.Headers;
        // The GitHub API requires two headers.
        _httpClient.DefaultRequestHeaders.Add(
            HeaderNames.Accept, "application/vnd.github.v3+json");
        _httpClient.DefaultRequestHeaders.Add(
            HeaderNames.UserAgent, "HttpRequestsSample");
    }

    public async Task<IEnumerable<GitHubBranch>?> GetAspNetCoreDocsBranchesAsync() =>
        await _httpClient.GetFromJsonAsync<IEnumerable<GitHubBranch>>(
            "repos/dotnet/AspNetCore.Docs/branches");
}

Az előző kódban:

  • A konfiguráció át lesz helyezve a beírt ügyfélbe.
  • A megadott HttpClient példány privát mezőként van tárolva.

Olyan API-specifikus metódusok hozhatók létre, amelyek elérhetővé HttpClient teszik a funkciókat. A GetAspNetCoreDocsBranches módszer például kódokat kapszuláz, hogy dokumentációt kérjen le a GitHub-ágakból.

A következő kód AddHttpClient hívást a Program.cs-ben a GitHubService típusú ügyfélosztály regisztrálására használják:

builder.Services.AddHttpClient<GitHubService>();

A beírt ügyfél átmenetiként van regisztrálva a DI-ben. Az előző kódban AddHttpClient átmeneti szolgáltatásként regisztrál GitHubService . Ez a regisztráció egy gyári módszert használ a következő célokra:

  1. Hozza létre a HttpClient egy példányát.
  2. Hozzon létre egy GitHubService példányt, amelynek konstruktorába adja át a HttpClient példányt.

A beírt ügyfél közvetlenül injektálható és felhasználható:

public class TypedClientModel : PageModel
{
    private readonly GitHubService _gitHubService;

    public TypedClientModel(GitHubService gitHubService) =>
        _gitHubService = gitHubService;

    public IEnumerable<GitHubBranch>? GitHubBranches { get; set; }

    public async Task OnGet()
    {
        try
        {
            GitHubBranches = await _gitHubService.GetAspNetCoreDocsBranchesAsync();
        }
        catch (HttpRequestException)
        {
            // ...
        }
    }
}

A típusos ügyfél konfigurációja a regisztráció Program.cs során is megadható a típusos ügyfél konstruktora helyett.

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

    // ...
});

Létrehozott ügyfelek

IHttpClientFactory külső kódtárakkal, például a Refittel együtt használható. A Refit egy REST .NET-kódtár. Az API-kat élő felületekké alakítja REST . Hívja meg a AddRefitClient-t egy felület dinamikus implementációjának létrehozásához, amely a HttpClient-t használja a külső HTTP-hívások végrehajtására.

Az egyéni felület a külső API-t jelöli:

public interface IGitHubClient
{
    [Get("/repos/dotnet/AspNetCore.Docs/branches")]
    Task<IEnumerable<GitHubBranch>> GetAspNetCoreDocsBranchesAsync();
}

Hívja meg AddRefitClient a dinamikus implementáció létrehozásához, majd hívja meg ConfigureHttpClient az alapjául szolgáló HttpClient konfigurálásához.

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

        // using Microsoft.Net.Http.Headers;
        // The GitHub API requires two headers.
        httpClient.DefaultRequestHeaders.Add(
            HeaderNames.Accept, "application/vnd.github.v3+json");
        httpClient.DefaultRequestHeaders.Add(
            HeaderNames.UserAgent, "HttpRequestsSample");
    });

DI használatával érheti el a dinamikus implementációt IGitHubClient:

public class RefitModel : PageModel
{
    private readonly IGitHubClient _gitHubClient;

    public RefitModel(IGitHubClient gitHubClient) =>
        _gitHubClient = gitHubClient;

    public IEnumerable<GitHubBranch>? GitHubBranches { get; set; }

    public async Task OnGet()
    {
        try
        {
            GitHubBranches = await _gitHubClient.GetAspNetCoreDocsBranchesAsync();
        }
        catch (ApiException)
        {
            // ...
        }
    }
}

POST, PUT és DELETE kérések létrehozása

Az előző példákban minden HTTP-kérés a GET HTTP parancsot használja. HttpClient egyéb HTTP-parancsokat is támogat, többek között a következőket:

  • POST
  • PUT
  • DELETE
  • PATCH

A támogatott HTTP-parancsok teljes listáját lásd HttpMethod: .

Az alábbi példa bemutatja, hogyan lehet HTTP POST-kérelmet küldeni:

public async Task CreateItemAsync(TodoItem todoItem)
{
    var todoItemJson = new StringContent(
        JsonSerializer.Serialize(todoItem),
        Encoding.UTF8,
        Application.Json); // using static System.Net.Mime.MediaTypeNames;

    using var httpResponseMessage =
        await _httpClient.PostAsync("/api/TodoItems", todoItemJson);

    httpResponseMessage.EnsureSuccessStatusCode();
}

Az előző kódban a CreateItemAsync metódus:

  • Szerializálja a TodoItem paramétert JSON formátumba, a System.Text.Json használatával.
  • Létrehoz egy példányt StringContent a szerializált JSON becsomagolásához a HTTP-kérés törzsébe való küldéshez.
  • A JSON-tartalomnak a megadott URL-címre való elküldésére irányuló hívások PostAsync . Ez egy relatív URL-cím, amely hozzáadódik a HttpClient.BaseAddresshez.
  • Ha a válasz állapotkódja nem jelzi a sikert, a hívás EnsureSuccessStatusCode kivételt jelez.

HttpClient más típusú tartalmakat is támogat. Például: MultipartContent és StreamContent. A támogatott tartalmak teljes listáját lásd HttpContent: .

Az alábbi példa egy HTTP PUT-kérést mutat be:

public async Task SaveItemAsync(TodoItem todoItem)
{
    var todoItemJson = new StringContent(
        JsonSerializer.Serialize(todoItem),
        Encoding.UTF8,
        Application.Json);

    using var httpResponseMessage =
        await _httpClient.PutAsync($"/api/TodoItems/{todoItem.Id}", todoItemJson);

    httpResponseMessage.EnsureSuccessStatusCode();
}

Az előző kód hasonló a POST-példához. A SaveItemAsync metódus a PutAsync-t hívja, nem pedig a PostAsync-t.

Az alábbi példa egy HTTP DELETE-kérést mutat be:

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

    httpResponseMessage.EnsureSuccessStatusCode();
}

Az előző kódban a DeleteItemAsync metódus meghívja a DeleteAsync metódust. Mivel a HTTP DELETE-kérelmek általában nem tartalmaznak törzset, a DeleteAsync metódus nem biztosít olyan túlterhelést, amely elfogad egy HttpContent példányt.

Ha többet szeretne megtudni a különböző HTTP-parancsok a HttpClient használatáról, látogasson el a(z) HttpClient oldalra.

Kimenő kérelem köztes szoftvere

HttpClient a kimenő HTTP-kérésekhez összekapcsolható kezelők delegálásának koncepciója. IHttpClientFactory:

  • Leegyszerűsíti az egyes elnevezett ügyfelekre alkalmazni kívánt kezelők meghatározását.
  • Támogatja a több kezelő regisztrációját és láncolását egy kimenő kérelem köztesszoftver-folyamat létrehozásához. Ezek a kezelők a kimenő kérés előtt és után is el tudják végezni a munkát. Ez a minta:
    • Hasonló az ASP.NET Core bejövő middleware-folyamatához.
    • Mechanizmust biztosít a HTTP-kérelmekkel kapcsolatos horizontális problémák kezelésére, például:
      • gyorsítótárazás
      • hibakezelés
      • szerializálás
      • naplózás

Delegáló kezelő létrehozása:

  • Származtasson innen: DelegatingHandler.
  • Felülírás SendAsync. Hajtsa végre a kódot, mielőtt átadja a kérést a folyamat következő kezelőjének:
public class ValidateHeaderHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (!request.Headers.Contains("X-API-KEY"))
        {
            return new HttpResponseMessage(HttpStatusCode.BadRequest)
            {
                Content = new StringContent(
                    "The API key header X-API-KEY is required.")
            };
        }

        return await base.SendAsync(request, cancellationToken);
    }
}

Az előző kód ellenőrzi, hogy a X-API-KEY fejléc szerepel-e a kérelemben. Ha X-API-KEY hiányzik, visszatér BadRequest.

Több kezelő is hozzáadható a következővel rendelkező HttpClientkonfigurációhozMicrosoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandler:

builder.Services.AddTransient<ValidateHeaderHandler>();

builder.Services.AddHttpClient("HttpMessageHandler")
    .AddHttpMessageHandler<ValidateHeaderHandler>();

Az előző kódban a ValidateHeaderHandler be van regisztrálva a DI-hez. A regisztráció után a AddHttpMessageHandler meghívható úgy, hogy megadjuk a kezelő típusát.

Több kezelő is regisztrálható abban a sorrendben, amelyet végre kell hajtaniuk. Minden kezelő burkolja a következő kezelőt, amíg végül a HttpClientHandler végrehajtja a kérést.

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

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

Az előző kódban előbb fut, SampleHandler1 mielőtt SampleHandler2.

A kimenő kérelem-middleware-ben használd a DI-t

Új delegálási kezelő létrehozásakor IHttpClientFactory a kezelő konstruktorparamétereinek teljesítéséhez a DI-t használja. IHttpClientFactory külön DI-hatókört hoz létre az egyes kezelők számára, ami meglepő viselkedéshez vezethet, ha egy kezelő egy hatókörön belüli szolgáltatást használ.

Vegyük például a következő felületet és annak implementációját, amely egy feladatot egy azonosítóval rendelkező műveletként jelöl: OperationId

public interface IOperationScoped
{
    string OperationId { get; }
}

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

Ahogy a neve is sugallja, IOperationScoped egy hatókörön belüli élettartammal van regisztrálva a DI-ben :

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

A következő delegáló kezelő felhasználja IOperationScoped az X-OPERATION-ID fejléc beállításához a kimenő kérésnél.

public class OperationHandler : DelegatingHandler
{
    private readonly IOperationScoped _operationScoped;

    public OperationHandler(IOperationScoped operationScoped) =>
        _operationScoped = operationScoped;

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.Headers.Add("X-OPERATION-ID", _operationScoped.OperationId);

        return await base.SendAsync(request, cancellationToken);
    }
}

A HttpRequestsSample letöltésben nyissa meg a /Operation-t, és frissítse az oldalt. A kérelem hatókörének értéke az egyes kérések esetében módosul, de a kezelő hatókörének értéke csak 5 másodpercenként változik.

A kezelők bármilyen hatókörű szolgáltatástól függhetnek. Azok a szolgáltatások, amelyektől a kezelők függenek, a kezelő ártalmatlanításakor lesznek megsemmisítve.

A kérésenkénti állapotok üzenetkezelőkkel való megosztásához használja az alábbi módszerek egyikét:

Polly-alapú kezelők használata

IHttpClientFactory integrálható a harmadik féltől származó Polly könyvtárral. A Polly egy átfogó rugalmassági és átmeneti hibakezelési kódtár a .NET-hez. Lehetővé teszi, hogy a fejlesztők folyékonyan és szálbiztosan fejezzék ki az olyan szabályzatokat, mint az Újrapróbálkozás, a Megszakító, az Időtúllépés, a Válaszfal elkülönítése és a Fallback.

A bővítménymetelyek lehetővé teszik a Polly-szabályzatok használatát konfigurált HttpClient példányokkal. A Polly-bővítmények támogatják a Polly-alapú kezelők ügyfelekhez való hozzáadását. A Pollyhoz a Microsoft.Extensions.Http.Polly NuGet csomag szükséges.

Átmeneti hibák kezelése

A hibák általában akkor fordulnak elő, ha a külső HTTP-hívások átmenetiek. AddTransientHttpErrorPolicy lehetővé teszi, hogy egy szabályzat definiálva legyen az átmeneti hibák kezelésére. A következő válaszok kezelésére konfigurált AddTransientHttpErrorPolicy szabályzatok:

AddTransientHttpErrorPolicy hozzáférést biztosít egy PolicyBuilder olyan objektumhoz, amely egy lehetséges átmeneti hibát jelképező hibák kezelésére van konfigurálva:

builder.Services.AddHttpClient("PollyWaitAndRetry")
    .AddTransientHttpErrorPolicy(policyBuilder =>
        policyBuilder.WaitAndRetryAsync(
            3, retryNumber => TimeSpan.FromMilliseconds(600)));

Az előző kódban egy WaitAndRetryAsync szabályzat van definiálva. A sikertelen kérelmeket a rendszer akár háromszor is újrapróbálkozza a kísérletek között 600 ms késleltetéssel.

Szabályzatok dinamikus kiválasztása

A kiterjesztési metódusok például AddPolicyHandler Polly alapú kezelők hozzáadására szolgálnak. A következő AddPolicyHandler túlterhelési metódus (vagy függvény) vizsgálja meg a kérelmet annak eldöntésére, hogy melyik szabályzatot alkalmazza.

var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
    TimeSpan.FromSeconds(10));
var longTimeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
    TimeSpan.FromSeconds(30));

builder.Services.AddHttpClient("PollyDynamic")
    .AddPolicyHandler(httpRequestMessage =>
        httpRequestMessage.Method == HttpMethod.Get ? timeoutPolicy : longTimeoutPolicy);

Az előző kódban, ha a kimenő kérés HTTP GET, a rendszer 10 másodperces időtúllépést alkalmaz. Bármely más HTTP-metódus esetében 30 másodperces időtúllépést használunk.

Több Polly-kezelő hozzáadása

Gyakori a Polly-szabályok beágyazása.

builder.Services.AddHttpClient("PollyMultiple")
    .AddTransientHttpErrorPolicy(policyBuilder =>
        policyBuilder.RetryAsync(3))
    .AddTransientHttpErrorPolicy(policyBuilder =>
        policyBuilder.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));

Az előző példában:

  • A rendszer két kezelőt ad hozzá.
  • Az első kezelő AddTransientHttpErrorPolicy segítségével ad hozzá egy újrapróbálkozási szabályzatot. A sikertelen kérelmeket a rendszer akár háromszor is újrapróbálkozza.
  • A második AddTransientHttpErrorPolicy hívás egy áramkör-megszakító házirendet ad hozzá. További külső kérések 30 másodpercig le lesznek tiltva, ha 5 sikertelen kísérlet egymás után történik. Az áramkör-megszakító házirendjei állapotalapúak. Az ügyfélen keresztüli összes hívás ugyanazt az áramkör állapotot osztja meg.

Szabályzatok hozzáadása a Polly-beállításjegyzékből

A rendszeresen használt szabályzatok kezelésének egyik módszere az, hogy egyszeri definiálásuk után regisztrálja azokat egy PolicyRegistry. Például:

var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
    TimeSpan.FromSeconds(10));
var longTimeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
    TimeSpan.FromSeconds(30));

var policyRegistry = builder.Services.AddPolicyRegistry();

policyRegistry.Add("Regular", timeoutPolicy);
policyRegistry.Add("Long", longTimeoutPolicy);

builder.Services.AddHttpClient("PollyRegistryRegular")
    .AddPolicyHandlerFromRegistry("Regular");

builder.Services.AddHttpClient("PollyRegistryLong")
    .AddPolicyHandlerFromRegistry("Long");

Az előző kódban:

  • A Polly-beállításjegyzékhez két házirendet Regular és Long adnak hozzá.
  • AddPolicyHandlerFromRegistry az egyes elnevezett ügyfeleket úgy konfigurálja, hogy ezeket a házirendeket a Polly-beállításjegyzékből használják.

További információért a IHttpClientFactory és a Polly-integrációkról, lásd a Polly wiki oldalt.

HttpClient és élettartam-kezelése

Minden alkalommal egy új HttpClient példányt ad vissza, amikor a CreateClient-n meghívják a IHttpClientFactory-t. Nevesített ügyfelenként jön létre egy HttpMessageHandler. A gyár kezeli az HttpMessageHandler példányok élettartamát.

IHttpClientFactory összegyűjti a gyár által létrehozott HttpMessageHandler példányokat az erőforrás-felhasználás csökkentése érdekében. A HttpMessageHandler példányok újra felhasználhatók a készletből egy új HttpClient példány létrehozásakor, ha az élettartama még nem járt le.

A kezelők készletezése kívánatos, mivel az egyes kezelők általában saját mögöttes HTTP-kapcsolatokat kezelnek. Ha a szükségesnél több kezelőt hoz létre, az a kapcsolat késéséhez vezethet. Egyes kezelők emellett határozatlan ideig nyitva tartják a kapcsolatokat, ami megakadályozhatja, hogy a kezelő reagáljon a DNS (tartománynévrendszer) változásaira.

Az alapértelmezett kezelő élettartama két perc. Az alapértelmezett érték felülírható nevesített ügyfél alapján:

builder.Services.AddHttpClient("HandlerLifetime")
    .SetHandlerLifetime(TimeSpan.FromMinutes(5));

HttpClient a példányok általában olyan .NET-objektumokként kezelhetők, amelyek nem igényelnek ártalmatlanítást. A felszabadítás megszakítja a kimenő kérelmeket, és garantálja, hogy az adott HttpClient példány nem használható a hívás után Dispose. IHttpClientFactory nyomon követi és megsemmisíti a HttpClient példányok által használt erőforrásokat.

Egyetlen HttpClient példány hosszú ideig történő életben tartása IHttpClientFactory bevezetése előtti gyakori minta. Ez a minta szükségtelenné válik az IHttpClientFactory rendszerre történő migrálás után.

Az IHttpClientFactory alternatívái

A DI-kompatibilis alkalmazások használata IHttpClientFactory elkerüli a következőt:

  • Erőforrás-kimerülési problémák az HttpMessageHandler példányok összevonásával.
  • Régi DNS-problémákat orvosolhatunk az HttpMessageHandler példányok rendszeres időközönkénti újraindításával.

Az előző problémákat többféleképpen is meg lehet oldani egy hosszú élettartamú SocketsHttpHandler példány használatával.

  • Hozzon létre egy SocketsHttpHandler példányt, amikor az alkalmazás elindul, és használja azt az alkalmazás teljes élettartama alatt.
  • Konfiguráljon PooledConnectionLifetime egy megfelelő értékre a DNS frissítési ideje alapján.
  • Hozzon létre HttpClient példányokat new HttpClient(handler, disposeHandler: false) szükség szerint.

Az előző megközelítések hasonló módon oldják meg azokat az erőforrás-kezelési problémákat, amelyeket a IHttpClientFactory megold.

  • A SocketsHttpHandler megosztja a kapcsolatokat a HttpClient példányok között. Ez a megosztás megakadályozza a foglalatok kimerülését.
  • A SocketsHttpHandler a kapcsolatokat PooledConnectionLifetime szerint ciklusokba rendezi az elavult DNS-problémák elkerülése érdekében.

Logging

Az ügyfelek, akik a IHttpClientFactory segítségével lettek létrehozva, naplóüzeneteket rögzítenek az összes kéréshez. A naplózási konfigurációban engedélyezze a megfelelő információs szintet az alapértelmezett naplóüzenetek megtekintéséhez. A további naplózás, például a kérelemfejlécek naplózása csak nyomkövetési szinten szerepel.

Az egyes ügyfelekhez használt naplókategória tartalmazza az ügyfél nevét. Egy MyNamedClient nevű ügyfél például naplózza a "System.Net.Http.HttpClient" kategóriával rendelkező üzeneteket. MyNamedClient. LogicalHandler". A LogicalHandlerrel utótaggal ellátott üzenetek a kérelemkezelő folyamaton kívülre kerülnek. A kéréskor a rendszer az üzeneteket naplózza, mielőtt a folyamat többi kezelője feldolgozta volna. A válaszban az üzenetek naplózva lesznek, miután bármely más folyamatkezelő megkapta a választ.

A naplózás a kérelemkezelő folyamaton belül is megtörténik. A MyNamedClient példában ezek az üzenetek a "System.Net.Http.HttpClient" naplókategória szerint vannak naplózva. MyNamedClient. ClientHandler". A kérés esetében ez az összes többi kezelő lefutása után, és közvetlenül a kérés elküldése előtt fordul elő. A válaszban ez a naplózás a válasz állapotát is tartalmazza, mielőtt az áthalad a kezelőfolyamaton.

A folyamaton kívüli és a folyamaton belüli naplózás engedélyezése lehetővé teszi a többi folyamatkezelő által végrehajtott módosítások ellenőrzését. Ez a kérelemfejlécek vagy a válaszállapot-kód módosításait is tartalmazhatja.

Az ügyfél nevének beleszámításával a naplókategória lehetővé teszi a naplószűrést adott nevesített ügyfelek esetében.

A HttpMessageHandler konfigurálása

Szükség lehet az ügyfél által használt belső HttpMessageHandler konfiguráció szabályozására.

A rendszer névvel ellátott vagy beírt ügyfelek hozzáadásakor ad vissza egy IHttpClientBuilder hibát. A ConfigurePrimaryHttpMessageHandler bővítménymetódus használható meghatalmazott definiálására. A meghatalmazott az ügyfél által használt elsődleges HttpMessageHandler kiszolgáló létrehozásához és konfigurálásához használható:

builder.Services.AddHttpClient("ConfiguredHttpMessageHandler")
    .ConfigurePrimaryHttpMessageHandler(() =>
        new HttpClientHandler
        {
            AllowAutoRedirect = true,
            UseDefaultCredentials = true
        });

Cookies

A készletezett HttpMessageHandler példányoknál CookieContainer objektumok megosztása történik. A nem várt CookieContainer objektummegosztás gyakran helytelen kódot eredményez. A cookie-kat igénylő alkalmazások esetében fontolja meg az alábbiakat:

  • Az automatikus cookie kezelés letiltása
  • Elkerülve IHttpClientFactory

Hívás ConfigurePrimaryHttpMessageHandler az automatikus cookie kezelés letiltására:

builder.Services.AddHttpClient("NoAutomaticCookies")
    .ConfigurePrimaryHttpMessageHandler(() =>
        new HttpClientHandler
        {
            UseCookies = false
        });

Az IHttpClientFactory használata konzolalkalmazásban

Egy konzolalkalmazásban adja hozzá a következő csomaghivatkozásokat a projekthez:

Az alábbi példában:

  • IHttpClientFactory és GitHubService regisztrálva vannak az Általános Gazdagép szolgáltatástárolójában.
  • A GitHubService-t a DI kéri, amely viszont egy példányt kér a IHttpClientFactory-ből.
  • GitHubService IHttpClientFactory-t használ, hogy létrehozzon egy HttpClient példányt, amit a GitHub dokumentumok ágainak lekérésére használ.
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

var host = new HostBuilder()
    .ConfigureServices(services =>
    {
        services.AddHttpClient();
        services.AddTransient<GitHubService>();
    })
    .Build();

try
{
    var gitHubService = host.Services.GetRequiredService<GitHubService>();
    var gitHubBranches = await gitHubService.GetAspNetCoreDocsBranchesAsync();

    Console.WriteLine($"{gitHubBranches?.Count() ?? 0} GitHub Branches");

    if (gitHubBranches is not null)
    {
        foreach (var gitHubBranch in gitHubBranches)
        {
            Console.WriteLine($"- {gitHubBranch.Name}");
        }
    }
}
catch (Exception ex)
{
    host.Services.GetRequiredService<ILogger<Program>>()
        .LogError(ex, "Unable to load branches from GitHub.");
}

public class GitHubService
{
    private readonly IHttpClientFactory _httpClientFactory;

    public GitHubService(IHttpClientFactory httpClientFactory) =>
        _httpClientFactory = httpClientFactory;

    public async Task<IEnumerable<GitHubBranch>?> GetAspNetCoreDocsBranchesAsync()
    {
        var httpRequestMessage = new HttpRequestMessage(
            HttpMethod.Get,
            "https://api.github.com/repos/dotnet/AspNetCore.Docs/branches")
        {
            Headers =
            {
                { "Accept", "application/vnd.github.v3+json" },
                { "User-Agent", "HttpRequestsConsoleSample" }
            }
        };

        var httpClient = _httpClientFactory.CreateClient();
        var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);

        httpResponseMessage.EnsureSuccessStatusCode();

        using var contentStream =
            await httpResponseMessage.Content.ReadAsStreamAsync();
        
        return await JsonSerializer.DeserializeAsync
            <IEnumerable<GitHubBranch>>(contentStream);
    }
}

public record GitHubBranch(
    [property: JsonPropertyName("name")] string Name);

Élőfej propagálása köztes szoftver

A fejléc átvitele egy ASP.NET Core middleware, amely a HTTP-fejléceket átviszi a bejövő kérésből a kimenő HttpClient kérésekbe. A fejlécpropagálás használata:

  • Telepítse a Microsoft.AspNetCore.HeaderPropagation csomagot.

  • Konfigurálja a HttpClient-t és a köztesszoftver-folyamatot a Program.cs-ben.

    // Add services to the container.
    builder.Services.AddControllers();
    
    builder.Services.AddHttpClient("PropagateHeaders")
        .AddHeaderPropagation();
    
    builder.Services.AddHeaderPropagation(options =>
    {
        options.Headers.Add("X-TraceId");
    });
    
    var app = builder.Build();
    
    // Configure the HTTP request pipeline.
    app.UseHttpsRedirection();
    
    app.UseHeaderPropagation();
    
    app.MapControllers();
    
  • Kimenő kérések létrehozása a konfigurált HttpClient példány használatával, amely tartalmazza a hozzáadott fejléceket.

További erőforrások

Kirk Larkin, Steve Gordon, Glenn Condron és Ryan Nowak.

Egy IHttpClientFactory regisztrálható és használható HttpClient példányok konfigurálására és létrehozására egy alkalmazásban. IHttpClientFactory a következő előnyöket kínálja:

  • Központi helyet biztosít a logikai HttpClient-példányok elnevezéséhez és konfigurálásához. Például egy github nevű ügyfél regisztrálható és konfigurálható a GitHub eléréséhez. Az alapértelmezett ügyfél regisztrálható az általános hozzáféréshez.
  • Kodifikálja a kimenő köztes szoftver fogalmát a HttpClient delegáló kezelők használatával. Bővítményeket biztosít a Polly-alapú köztes szoftverhez, hogy kihasználhassa a kezelők delegálásának előnyeit a HttpClient.
  • A mögöttes HttpClientMessageHandler példányok készletezését és élettartamát kezeli. Az automatikus felügyelet elkerüli az élettartamok manuális kezelésekor HttpClient előforduló gyakori DNS-(tartománynévrendszer-) problémákat.
  • Konfigurálható naplózási felületet (via ILogger) ad hozzá a gyár által létrehozott ügyfeleken keresztül küldött összes kéréshez.

Mintakód megtekintése vagy letöltése (hogyan töltsük le).

A jelen témakör verziójának mintakódja a HTTP-válaszokban visszaadott JSON-tartalom deszerializálására szolgál System.Text.Json . Azoknál a mintáknál, amelyek Json.NET és ReadAsAsync<T>-et használnak, a verzióválasztóval válassza ki ennek a témakörnek a 2.x verzióját.

Használati minták

Az alkalmazásokban többféleképpen IHttpClientFactory is használható:

A legjobb módszer az alkalmazás követelményeitől függ.

Alapszintű használat

IHttpClientFactory a következő hívással AddHttpClientregisztrálható:

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.

Egy IHttpClientFactory kérhető függőséginjektálással (DI). A következő kód IHttpClientFactory-t használ egy HttpClient példány létrehozásához.

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

Az előző példához hasonló használat IHttpClientFactory jó módszer egy meglévő alkalmazás újrabontására. Nincs hatással a használat módjára HttpClient . Azokon a helyeken, ahol HttpClient példányok jönnek létre egy meglévő alkalmazásban, cserélje le ezeket az előfordulásokat CreateClient hívásokkal.

Névvel ellátott ügyfelek

A nevesített ügyfelek akkor jó választásnak számítanak, ha:

  • Az alkalmazásnak számos különböző módja van HttpClient használatára.
  • Sok HttpClientmás konfigurációval rendelkezik.

Egy megnevezett HttpClient konfigurációja meghatározható a következő helyen, regisztráció során: 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");
});

Az előző kódban az ügyfél a következőkkel van konfigurálva:

  • Az alapcím https://api.github.com/.
  • A GitHub API használatához két fejléc szükséges.

CreateClient

Minden alkalommal, amikor CreateClient hívásra kerül:

  • Létrejön egy HttpClient új példány.
  • A rendszer meghívja a konfigurációs műveletet.

Névvel ellátott ügyfél létrehozásához adja át az ügyfél nevét a következőbe 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>();
        }
    }
}

Az előző kódban a kérésnek nem kell állomásnevet megadnia. A kód csak az elérési utat tudja átadni, mivel a rendszer az ügyfélhez konfigurált alapcímet használja.

Beírt ügyfelek

Beírt ügyfelek:

  • A névvel ellátott ügyfelekkel azonos képességeket biztosíthat anélkül, hogy sztringeket kellene használnia kulcsként.
  • Az IntelliSense és a fordító segítséget nyújtanak a kliensek használata során.
  • Adjon meg egyetlen helyet egy adott HttpClienthely konfigurálásához és használatához. Például használható egyetlen gépelt ügyfél:
    • Egyetlen háttérvégpont esetén.
    • A végponttal kapcsolatos összes logika beágyazása.
  • Együttműködhet a DI-vel, és szükség esetén injektálható az alkalmazásban.

A gépelt ügyfél egy HttpClient paramétert fogad el a konstruktorában.

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

Az előző kódban:

  • A konfiguráció át lesz helyezve a beírt ügyfélbe.
  • Az HttpClient objektum nyilvános tulajdonságként van közzétéve.

Olyan API-specifikus metódusok hozhatók létre, amelyek elérhetővé HttpClient teszik a funkciókat. Az GetAspNetDocsIssues például kódot ágyaz be a nyitott problémák lekéréséhez.

A következő kód meghívja a AddHttpClient a/t Startup.ConfigureServices-ben/-ban egy típusos ügyfélosztály regisztrálására:

services.AddHttpClient<GitHubService>();

A beírt ügyfél átmenetiként van regisztrálva a DI-ben. Az előző kódban AddHttpClient átmeneti szolgáltatásként regisztrál GitHubService . Ez a regisztráció egy gyári módszert használ a következő célokra:

  1. Hozza létre a HttpClient egy példányát.
  2. Hozzon létre egy GitHubService példányt, amelynek konstruktorába adja át a HttpClient példányt.

A beírt ügyfél közvetlenül injektálható és felhasználható:

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

A típusos kliens konfigurációja a regisztráció Startup.ConfigureServices során adható meg, nem pedig a típusos kliens konstruktorában.

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

A HttpClient típusos kliensben beágyazható. Ahelyett, hogy tulajdonságként felfedte volna, definiáljon egy metódust, amely belsőleg hívja meg a HttpClient példányt:

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

Az előző kódban a rendszer egy privát mezőben tárolja az HttpClient adatokat. A hozzáférés a HttpClient nyilvános GetRepos módszer szerint történik.

Létrehozott ügyfelek

IHttpClientFactory külső kódtárakkal, például a Refittel együtt használható. A Refit egy REST .NET-kódtár. Az API-kat élő felületekké alakítja REST . Az interfész implementációját a RestService dinamikusan hozza létre, miközben a HttpClient külső HTTP-hívások végrehajtására szolgál.

Egy felület és egy válasz definiálva van, amely a külső API-t és annak válaszát jelöli:

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

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

Egy beírt ügyfél hozzáadható a Refit használatával a megvalósítás létrehozásához:

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

A definiált felület szükség esetén felhasználható a DI és a Refit által biztosított implementációval:

[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 és DELETE kérések létrehozása

Az előző példákban minden HTTP-kérés a GET HTTP parancsot használja. HttpClient egyéb HTTP-parancsokat is támogat, többek között a következőket:

  • POST
  • PUT
  • DELETE
  • PATCH

A támogatott HTTP-parancsok teljes listáját lásd HttpMethod: .

Az alábbi példa bemutatja, hogyan lehet HTTP POST-kérelmet küldeni:

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

Az előző kódban a CreateItemAsync metódus:

  • Szerializálja a TodoItem paramétert JSON formátumba, a System.Text.Json használatával. A szerializálási folyamat konfigurálásához a JsonSerializerOptions egy példányát használja.
  • Létrehoz egy példányt StringContent a szerializált JSON becsomagolásához a HTTP-kérés törzsébe való küldéshez.
  • A JSON-tartalomnak a megadott URL-címre való elküldésére irányuló hívások PostAsync . Ez egy relatív URL-cím, amely hozzáadódik a HttpClient.BaseAddresshez.
  • Felhívja EnsureSuccessStatusCode-t, hogy kivételt váltson ki, ha a válasz állapotkódja nem jelzi a sikert.

HttpClient más típusú tartalmakat is támogat. Például: MultipartContent és StreamContent. A támogatott tartalmak teljes listáját lásd HttpContent: .

Az alábbi példa egy HTTP PUT-kérést mutat be:

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

Az előző kód nagyon hasonlít a POST-példához. A SaveItemAsync metódus a PutAsync-t hívja, nem pedig a PostAsync-t.

Az alábbi példa egy HTTP DELETE-kérést mutat be:

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

    httpResponse.EnsureSuccessStatusCode();
}

Az előző kódban a DeleteItemAsync metódus meghívja a DeleteAsync metódust. Mivel a HTTP DELETE-kérelmek általában nem tartalmaznak törzset, a DeleteAsync metódus nem biztosít olyan túlterhelést, amely elfogad egy HttpContent példányt.

Ha többet szeretne megtudni a különböző HTTP-parancsok a HttpClient használatáról, látogasson el a(z) HttpClient oldalra.

Kimenő kérelem köztes szoftvere

HttpClient a kimenő HTTP-kérésekhez összekapcsolható kezelők delegálásának koncepciója. IHttpClientFactory:

  • Leegyszerűsíti az egyes elnevezett ügyfelekre alkalmazni kívánt kezelők meghatározását.
  • Támogatja a több kezelő regisztrációját és láncolását egy kimenő kérelem köztesszoftver-folyamat létrehozásához. Ezek a kezelők a kimenő kérés előtt és után is el tudják végezni a munkát. Ez a minta:
    • Hasonló az ASP.NET Core bejövő middleware-folyamatához.
    • Mechanizmust biztosít a HTTP-kérelmekkel kapcsolatos horizontális problémák kezelésére, például:
      • gyorsítótárazás
      • hibakezelés
      • szerializálás
      • naplózás

Delegáló kezelő létrehozása:

  • Származtasson innen: DelegatingHandler.
  • Felülírás SendAsync. Hajtsa végre a kódot, mielőtt átadja a kérést a folyamat következő kezelőjének:
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);
    }
}

Az előző kód ellenőrzi, hogy a X-API-KEY fejléc szerepel-e a kérelemben. Ha X-API-KEY hiányzik, visszatér BadRequest.

Több kezelő is hozzáadható a következővel rendelkező HttpClientkonfigurációhozMicrosoft.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.

Az előző kódban a ValidateHeaderHandler be van regisztrálva a DI-hez. A regisztráció után a AddHttpMessageHandler meghívható úgy, hogy megadjuk a kezelő típusát.

Több kezelő is regisztrálható abban a sorrendben, amelyet végre kell hajtaniuk. Minden kezelő burkolja a következő kezelőt, amíg végül a HttpClientHandler végrehajtja a kérést.

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

A kimenő kérelem-middleware-ben használd a DI-t

Új delegálási kezelő létrehozásakor IHttpClientFactory a kezelő konstruktorparamétereinek teljesítéséhez a DI-t használja. IHttpClientFactory külön DI-hatókört hoz létre az egyes kezelők számára, ami meglepő viselkedéshez vezethet, ha egy kezelő egy hatókörön belüli szolgáltatást használ.

Vegyük például a következő felületet és annak implementációját, amely egy feladatot egy azonosítóval rendelkező műveletként jelöl: OperationId

public interface IOperationScoped 
{
    string OperationId { get; }
}

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

Ahogy a neve is sugallja, IOperationScoped egy hatókörön belüli élettartammal van regisztrálva a DI-ben :

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 következő delegáló kezelő felhasználja IOperationScoped az X-OPERATION-ID fejléc beállításához a kimenő kérésnél.

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 A letöltésben keresse meg /Operation és frissítse a lapot. A kérelem hatókörének értéke az egyes kérések esetében módosul, de a kezelő hatókörének értéke csak 5 másodpercenként változik.

A kezelők bármilyen hatókörű szolgáltatástól függhetnek. Azok a szolgáltatások, amelyektől a kezelők függenek, a kezelő ártalmatlanításakor lesznek megsemmisítve.

A kérésenkénti állapotok üzenetkezelőkkel való megosztásához használja az alábbi módszerek egyikét:

Polly-alapú kezelők használata

IHttpClientFactory integrálható a harmadik féltől származó Polly könyvtárral. A Polly egy átfogó rugalmassági és átmeneti hibakezelési kódtár a .NET-hez. Lehetővé teszi, hogy a fejlesztők folyékonyan és szálbiztosan fejezzék ki az olyan szabályzatokat, mint az Újrapróbálkozás, a Megszakító, az Időtúllépés, a Válaszfal elkülönítése és a Fallback.

A bővítménymetelyek lehetővé teszik a Polly-szabályzatok használatát konfigurált HttpClient példányokkal. A Polly-bővítmények támogatják a Polly-alapú kezelők ügyfelekhez való hozzáadását. A Pollyhoz a Microsoft.Extensions.Http.Polly NuGet csomag szükséges.

Átmeneti hibák kezelése

A hibák általában akkor fordulnak elő, ha a külső HTTP-hívások átmenetiek. AddTransientHttpErrorPolicy lehetővé teszi, hogy egy szabályzat definiálva legyen az átmeneti hibák kezelésére. A következő válaszok kezelésére konfigurált AddTransientHttpErrorPolicy szabályzatok:

AddTransientHttpErrorPolicy hozzáférést biztosít egy PolicyBuilder olyan objektumhoz, amely egy lehetséges átmeneti hibát jelképező hibák kezelésére van konfigurálva:

public void ConfigureServices(IServiceCollection services)
{           
    services.AddHttpClient<UnreliableEndpointCallerService>()
        .AddTransientHttpErrorPolicy(p => 
            p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));

    // Remaining code deleted for brevity.

Az előző kódban egy WaitAndRetryAsync szabályzat van definiálva. A sikertelen kérelmeket a rendszer akár háromszor is újrapróbálkozza a kísérletek között 600 ms késleltetéssel.

Szabályzatok dinamikus kiválasztása

A kiterjesztési metódusok például AddPolicyHandler Polly alapú kezelők hozzáadására szolgálnak. A következő AddPolicyHandler túlterhelési metódus (vagy függvény) vizsgálja meg a kérelmet annak eldöntésére, hogy melyik szabályzatot alkalmazza.

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

Az előző kódban, ha a kimenő kérés HTTP GET, a rendszer 10 másodperces időtúllépést alkalmaz. Bármely más HTTP-metódus esetében 30 másodperces időtúllépést használunk.

Több Polly-kezelő hozzáadása

Gyakori a Polly-szabályok beágyazása.

services.AddHttpClient("multiplepolicies")
    .AddTransientHttpErrorPolicy(p => p.RetryAsync(3))
    .AddTransientHttpErrorPolicy(
        p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));

Az előző példában:

  • A rendszer két kezelőt ad hozzá.
  • Az első kezelő AddTransientHttpErrorPolicy segítségével ad hozzá egy újrapróbálkozási szabályzatot. A sikertelen kérelmeket a rendszer akár háromszor is újrapróbálkozza.
  • A második AddTransientHttpErrorPolicy hívás egy áramkör-megszakító házirendet ad hozzá. További külső kérések 30 másodpercig le lesznek tiltva, ha 5 sikertelen kísérlet egymás után történik. Az áramkör-megszakító házirendjei állapotalapúak. Az ügyfélen keresztüli összes hívás ugyanazt az áramkör állapotot osztja meg.

Szabályzatok hozzáadása a Polly-beállításjegyzékből

A rendszeresen használt szabályzatok kezelésének egyik módszere az, hogy egyszeri definiálásuk után regisztrálja azokat egy PolicyRegistry.

A következő kódban:

  • A "normál" és a "hosszú" szabályzat hozzáadásra került.
  • AddPolicyHandlerFromRegistry hozzáadja a "normál" és a "hosszú" szabályzatokat a beállításjegyzékhez.
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.

További információért a IHttpClientFactory és a Polly-integrációkról, lásd a Polly wiki oldalt.

HttpClient és élettartam-kezelése

Minden alkalommal egy új HttpClient példányt ad vissza, amikor a CreateClient-n meghívják a IHttpClientFactory-t. Nevesített ügyfelenként jön létre egy HttpMessageHandler. A gyár kezeli az HttpMessageHandler példányok élettartamát.

IHttpClientFactory összegyűjti a gyár által létrehozott HttpMessageHandler példányokat az erőforrás-felhasználás csökkentése érdekében. A HttpMessageHandler példányok újra felhasználhatók a készletből egy új HttpClient példány létrehozásakor, ha az élettartama még nem járt le.

A kezelők készletezése kívánatos, mivel az egyes kezelők általában saját mögöttes HTTP-kapcsolatokat kezelnek. Ha a szükségesnél több kezelőt hoz létre, az a kapcsolat késéséhez vezethet. Egyes kezelők emellett határozatlan ideig nyitva tartják a kapcsolatokat, ami megakadályozhatja, hogy a kezelő reagáljon a DNS (tartománynévrendszer) változásaira.

Az alapértelmezett kezelő élettartama két perc. Az alapértelmezett érték felülírható nevesített ügyfél alapján:

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

    // Remaining code deleted for brevity.

HttpClient a példányok általában olyan .NET-objektumokként kezelhetők, amelyek nem igényelnek ártalmatlanítást. A felszabadítás megszakítja a kimenő kérelmeket, és garantálja, hogy az adott HttpClient példány nem használható a hívás után Dispose. IHttpClientFactory nyomon követi és megsemmisíti a HttpClient példányok által használt erőforrásokat.

Egyetlen HttpClient példány hosszú ideig történő életben tartása IHttpClientFactory bevezetése előtti gyakori minta. Ez a minta szükségtelenné válik az IHttpClientFactory rendszerre történő migrálás után.

Az IHttpClientFactory alternatívái

A DI-kompatibilis alkalmazások használata IHttpClientFactory elkerüli a következőt:

  • Erőforrás-kimerülési problémák az HttpMessageHandler példányok összevonásával.
  • Régi DNS-problémákat orvosolhatunk az HttpMessageHandler példányok rendszeres időközönkénti újraindításával.

Az előző problémákat többféleképpen is meg lehet oldani egy hosszú élettartamú SocketsHttpHandler példány használatával.

  • Hozzon létre egy SocketsHttpHandler példányt, amikor az alkalmazás elindul, és használja azt az alkalmazás teljes élettartama alatt.
  • Konfiguráljon PooledConnectionLifetime egy megfelelő értékre a DNS frissítési ideje alapján.
  • Hozzon létre HttpClient példányokat new HttpClient(handler, disposeHandler: false) szükség szerint.

Az előző megközelítések hasonló módon oldják meg azokat az erőforrás-kezelési problémákat, amelyeket a IHttpClientFactory megold.

  • A SocketsHttpHandler megosztja a kapcsolatokat a HttpClient példányok között. Ez a megosztás megakadályozza a foglalatok kimerülését.
  • A SocketsHttpHandler a kapcsolatokat PooledConnectionLifetime szerint ciklusokba rendezi az elavult DNS-problémák elkerülése érdekében.

Cookies

A készletezett HttpMessageHandler példányoknál CookieContainer objektumok megosztása történik. A nem várt CookieContainer objektummegosztás gyakran helytelen kódot eredményez. A cookie-kat igénylő alkalmazások esetében fontolja meg az alábbiakat:

  • Az automatikus cookie kezelés letiltása
  • Elkerülve IHttpClientFactory

Hívás ConfigurePrimaryHttpMessageHandler az automatikus cookie kezelés letiltására:

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

Logging

Az ügyfelek, akik a IHttpClientFactory segítségével lettek létrehozva, naplóüzeneteket rögzítenek az összes kéréshez. A naplózási konfigurációban engedélyezze a megfelelő információs szintet az alapértelmezett naplóüzenetek megtekintéséhez. A további naplózás, például a kérelemfejlécek naplózása csak nyomkövetési szinten szerepel.

Az egyes ügyfelekhez használt naplókategória tartalmazza az ügyfél nevét. Egy MyNamedClient nevű ügyfél például naplózza a "System.Net.Http.HttpClient" kategóriával rendelkező üzeneteket. MyNamedClient. LogicalHandler". A LogicalHandlerrel utótaggal ellátott üzenetek a kérelemkezelő folyamaton kívülre kerülnek. A kéréskor a rendszer az üzeneteket naplózza, mielőtt a folyamat többi kezelője feldolgozta volna. A válaszban az üzenetek naplózva lesznek, miután bármely más folyamatkezelő megkapta a választ.

A naplózás a kérelemkezelő folyamaton belül is megtörténik. A MyNamedClient példában ezek az üzenetek a "System.Net.Http.HttpClient" naplókategória szerint vannak naplózva. MyNamedClient. ClientHandler". A kérés esetében ez az összes többi kezelő lefutása után, és közvetlenül a kérés elküldése előtt fordul elő. A válaszban ez a naplózás a válasz állapotát is tartalmazza, mielőtt az áthalad a kezelőfolyamaton.

A folyamaton kívüli és a folyamaton belüli naplózás engedélyezése lehetővé teszi a többi folyamatkezelő által végrehajtott módosítások ellenőrzését. Ez a kérelemfejlécek vagy a válaszállapot-kód módosításait is tartalmazhatja.

Az ügyfél nevének beleszámításával a naplókategória lehetővé teszi a naplószűrést adott nevesített ügyfelek esetében.

A HttpMessageHandler konfigurálása

Szükség lehet az ügyfél által használt belső HttpMessageHandler konfiguráció szabályozására.

A rendszer névvel ellátott vagy beírt ügyfelek hozzáadásakor ad vissza egy IHttpClientBuilder hibát. A ConfigurePrimaryHttpMessageHandler bővítménymetódus használható meghatalmazott definiálására. A meghatalmazott az ügyfél által használt elsődleges HttpMessageHandler kiszolgáló létrehozásához és konfigurálásához használható:

public void ConfigureServices(IServiceCollection services)
{            
    services.AddHttpClient("configured-inner-handler")
        .ConfigurePrimaryHttpMessageHandler(() =>
        {
            return new HttpClientHandler()
            {
                AllowAutoRedirect = false,
                UseDefaultCredentials = true
            };
        });

    // Remaining code deleted for brevity.

Az IHttpClientFactory használata konzolalkalmazásban

Egy konzolalkalmazásban adja hozzá a következő csomaghivatkozásokat a projekthez:

Az alábbi példában:

  • IHttpClientFactory regisztrálva van a Generic Host szolgáltatáskonténerében.
  • MyService létrehoz egy ügyfél-előállító példányt a szolgáltatás alapján, amelyet egy HttpClient létrehozásához használnak. HttpClient weblap lekérésére szolgál.
  • Main létrehoz egy hatókört a szolgáltatás metódusának GetPage végrehajtásához, és írja a weblap tartalmának első 500 karakterét a konzolra.
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}";
            }
        }
    }
}

Élőfej propagálása köztes szoftver

A fejlécpropagálás egy ASP.NET Core köztes szoftver, amely a HTTP-fejléceket propagálja a bejövő kérésből a kimenő HTTP-ügyfélkérelmekbe. A fejlécpropagálás használata:

  • Hivatkozzon a Microsoft.AspNetCore.HeaderPropagation csomagra .

  • Konfigurálja a köztes szoftvert és HttpClient a(z) Startup sorban.

    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();
        });
    }
    
  • Az ügyfél tartalmazza a konfigurált fejléceket a kimenő kérelmekhez:

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

További erőforrások

Kirk Larkin, Steve Gordon, Glenn Condron és Ryan Nowak.

Egy IHttpClientFactory regisztrálható és használható HttpClient példányok konfigurálására és létrehozására egy alkalmazásban. IHttpClientFactory a következő előnyöket kínálja:

  • Központi helyet biztosít a logikai HttpClient-példányok elnevezéséhez és konfigurálásához. Például egy github nevű ügyfél regisztrálható és konfigurálható a GitHub eléréséhez. Az alapértelmezett ügyfél regisztrálható az általános hozzáféréshez.
  • Kodifikálja a kimenő köztes szoftver fogalmát a HttpClient delegáló kezelők használatával. Bővítményeket biztosít a Polly-alapú köztes szoftverhez, hogy kihasználhassa a kezelők delegálásának előnyeit a HttpClient.
  • A mögöttes HttpClientMessageHandler példányok készletezését és élettartamát kezeli. Az automatikus felügyelet elkerüli az élettartamok manuális kezelésekor HttpClient előforduló gyakori DNS-(tartománynévrendszer-) problémákat.
  • Konfigurálható naplózási felületet (via ILogger) ad hozzá a gyár által létrehozott ügyfeleken keresztül küldött összes kéréshez.

Mintakód megtekintése vagy letöltése (hogyan töltsük le).

A jelen témakör verziójának mintakódja a HTTP-válaszokban visszaadott JSON-tartalom deszerializálására szolgál System.Text.Json . Azoknál a mintáknál, amelyek Json.NET és ReadAsAsync<T>-et használnak, a verzióválasztóval válassza ki ennek a témakörnek a 2.x verzióját.

Használati minták

Az alkalmazásokban többféleképpen IHttpClientFactory is használható:

A legjobb módszer az alkalmazás követelményeitől függ.

Alapszintű használat

IHttpClientFactory a következő hívással AddHttpClientregisztrálható:

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.

Egy IHttpClientFactory kérhető függőséginjektálással (DI). A következő kód IHttpClientFactory-t használ egy HttpClient példány létrehozásához.

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

Az előző példához hasonló használat IHttpClientFactory jó módszer egy meglévő alkalmazás újrabontására. Nincs hatással a használat módjára HttpClient . Azokon a helyeken, ahol HttpClient példányok jönnek létre egy meglévő alkalmazásban, cserélje le ezeket az előfordulásokat CreateClient hívásokkal.

Névvel ellátott ügyfelek

A nevesített ügyfelek akkor jó választásnak számítanak, ha:

  • Az alkalmazásnak számos különböző módja van HttpClient használatára.
  • Sok HttpClientmás konfigurációval rendelkezik.

Egy megnevezett HttpClient konfigurációja meghatározható a következő helyen, regisztráció során: 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");
});

Az előző kódban az ügyfél a következőkkel van konfigurálva:

  • Az alapcím https://api.github.com/.
  • A GitHub API használatához két fejléc szükséges.

CreateClient

Minden alkalommal, amikor CreateClient hívásra kerül:

  • Létrejön egy HttpClient új példány.
  • A rendszer meghívja a konfigurációs műveletet.

Névvel ellátott ügyfél létrehozásához adja át az ügyfél nevét a következőbe 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>();
        }
    }
}

Az előző kódban a kérésnek nem kell állomásnevet megadnia. A kód csak az elérési utat tudja átadni, mivel a rendszer az ügyfélhez konfigurált alapcímet használja.

Beírt ügyfelek

Beírt ügyfelek:

  • A névvel ellátott ügyfelekkel azonos képességeket biztosíthat anélkül, hogy sztringeket kellene használnia kulcsként.
  • Az IntelliSense és a fordító segítséget nyújtanak a kliensek használata során.
  • Adjon meg egyetlen helyet egy adott HttpClienthely konfigurálásához és használatához. Például használható egyetlen gépelt ügyfél:
    • Egyetlen háttérvégpont esetén.
    • A végponttal kapcsolatos összes logika beágyazása.
  • Együttműködhet a DI-vel, és szükség esetén injektálható az alkalmazásban.

A gépelt ügyfél egy HttpClient paramétert fogad el a konstruktorában.

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

Ha szeretné, hogy a kódkommentárok angolon kívül más nyelvekre is le legyenek fordítva, jelezze nekünk a GitHub vitafórumkérdésénél.

Az előző kódban:

  • A konfiguráció át lesz helyezve a beírt ügyfélbe.
  • Az HttpClient objektum nyilvános tulajdonságként van közzétéve.

Olyan API-specifikus metódusok hozhatók létre, amelyek elérhetővé HttpClient teszik a funkciókat. Az GetAspNetDocsIssues például kódot ágyaz be a nyitott problémák lekéréséhez.

A következő kód meghívja a AddHttpClient a/t Startup.ConfigureServices-ben/-ban egy típusos ügyfélosztály regisztrálására:

services.AddHttpClient<GitHubService>();

A beírt ügyfél átmenetiként van regisztrálva a DI-ben. Az előző kódban AddHttpClient átmeneti szolgáltatásként regisztrál GitHubService . Ez a regisztráció egy gyári módszert használ a következő célokra:

  1. Hozza létre a HttpClient egy példányát.
  2. Hozzon létre egy GitHubService példányt, amelynek konstruktorába adja át a HttpClient példányt.

A beírt ügyfél közvetlenül injektálható és felhasználható:

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

A típusos kliens konfigurációja a regisztráció Startup.ConfigureServices során adható meg, nem pedig a típusos kliens konstruktorában.

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

A HttpClient típusos kliensben beágyazható. Ahelyett, hogy tulajdonságként felfedte volna, definiáljon egy metódust, amely belsőleg hívja meg a HttpClient példányt:

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

Az előző kódban a rendszer egy privát mezőben tárolja az HttpClient adatokat. A hozzáférés a HttpClient nyilvános GetRepos módszer szerint történik.

Létrehozott ügyfelek

IHttpClientFactory külső kódtárakkal, például a Refittel együtt használható. A Refit egy REST .NET-kódtár. Az API-kat élő felületekké alakítja REST . Az interfész implementációját a RestService dinamikusan hozza létre, miközben a HttpClient külső HTTP-hívások végrehajtására szolgál.

Egy felület és egy válasz definiálva van, amely a külső API-t és annak válaszát jelöli:

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

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

Egy beírt ügyfél hozzáadható a Refit használatával a megvalósítás létrehozásához:

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

A definiált felület szükség esetén felhasználható a DI és a Refit által biztosított implementációval:

[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 és DELETE kérések létrehozása

Az előző példákban minden HTTP-kérés a GET HTTP parancsot használja. HttpClient egyéb HTTP-parancsokat is támogat, többek között a következőket:

  • POST
  • PUT
  • DELETE
  • PATCH

A támogatott HTTP-parancsok teljes listáját lásd HttpMethod: .

Az alábbi példa bemutatja, hogyan lehet HTTP POST-kérelmet küldeni:

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

Az előző kódban a CreateItemAsync metódus:

  • Szerializálja a TodoItem paramétert JSON formátumba, a System.Text.Json használatával. A szerializálási folyamat konfigurálásához a JsonSerializerOptions egy példányát használja.
  • Létrehoz egy példányt StringContent a szerializált JSON becsomagolásához a HTTP-kérés törzsébe való küldéshez.
  • A JSON-tartalomnak a megadott URL-címre való elküldésére irányuló hívások PostAsync . Ez egy relatív URL-cím, amely hozzáadódik a HttpClient.BaseAddresshez.
  • Felhívja EnsureSuccessStatusCode-t, hogy kivételt váltson ki, ha a válasz állapotkódja nem jelzi a sikert.

HttpClient más típusú tartalmakat is támogat. Például: MultipartContent és StreamContent. A támogatott tartalmak teljes listáját lásd HttpContent: .

Az alábbi példa egy HTTP PUT-kérést mutat be:

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

Az előző kód nagyon hasonlít a POST-példához. A SaveItemAsync metódus a PutAsync-t hívja, nem pedig a PostAsync-t.

Az alábbi példa egy HTTP DELETE-kérést mutat be:

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

    httpResponse.EnsureSuccessStatusCode();
}

Az előző kódban a DeleteItemAsync metódus meghívja a DeleteAsync metódust. Mivel a HTTP DELETE-kérelmek általában nem tartalmaznak törzset, a DeleteAsync metódus nem biztosít olyan túlterhelést, amely elfogad egy HttpContent példányt.

Ha többet szeretne megtudni a különböző HTTP-parancsok a HttpClient használatáról, látogasson el a(z) HttpClient oldalra.

Kimenő kérelem köztes szoftvere

HttpClient a kimenő HTTP-kérésekhez összekapcsolható kezelők delegálásának koncepciója. IHttpClientFactory:

  • Leegyszerűsíti az egyes elnevezett ügyfelekre alkalmazni kívánt kezelők meghatározását.
  • Támogatja a több kezelő regisztrációját és láncolását egy kimenő kérelem köztesszoftver-folyamat létrehozásához. Ezek a kezelők a kimenő kérés előtt és után is el tudják végezni a munkát. Ez a minta:
    • Hasonló az ASP.NET Core bejövő middleware-folyamatához.
    • Mechanizmust biztosít a HTTP-kérelmekkel kapcsolatos horizontális problémák kezelésére, például:
      • gyorsítótárazás
      • hibakezelés
      • szerializálás
      • naplózás

Delegáló kezelő létrehozása:

  • Származtasson innen: DelegatingHandler.
  • Felülírás SendAsync. Hajtsa végre a kódot, mielőtt átadja a kérést a folyamat következő kezelőjének:
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);
    }
}

Az előző kód ellenőrzi, hogy a X-API-KEY fejléc szerepel-e a kérelemben. Ha X-API-KEY hiányzik, visszatér BadRequest.

Több kezelő is hozzáadható a következővel rendelkező HttpClientkonfigurációhozMicrosoft.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.

Az előző kódban a ValidateHeaderHandler be van regisztrálva a DI-hez. A regisztráció után a AddHttpMessageHandler meghívható úgy, hogy megadjuk a kezelő típusát.

Több kezelő is regisztrálható abban a sorrendben, amelyet végre kell hajtaniuk. Minden kezelő burkolja a következő kezelőt, amíg végül a HttpClientHandler végrehajtja a kérést.

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

A kimenő kérelem-middleware-ben használd a DI-t

Új delegálási kezelő létrehozásakor IHttpClientFactory a kezelő konstruktorparamétereinek teljesítéséhez a DI-t használja. IHttpClientFactory külön DI-hatókört hoz létre az egyes kezelők számára, ami meglepő viselkedéshez vezethet, ha egy kezelő egy hatókörön belüli szolgáltatást használ.

Vegyük például a következő felületet és annak implementációját, amely egy feladatot egy azonosítóval rendelkező műveletként jelöl: OperationId

public interface IOperationScoped 
{
    string OperationId { get; }
}

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

Ahogy a neve is sugallja, IOperationScoped egy hatókörön belüli élettartammal van regisztrálva a DI-ben :

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 következő delegáló kezelő felhasználja IOperationScoped az X-OPERATION-ID fejléc beállításához a kimenő kérésnél.

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 A letöltésben keresse meg /Operation és frissítse a lapot. A kérelem hatókörének értéke az egyes kérések esetében módosul, de a kezelő hatókörének értéke csak 5 másodpercenként változik.

A kezelők bármilyen hatókörű szolgáltatástól függhetnek. Azok a szolgáltatások, amelyektől a kezelők függenek, a kezelő ártalmatlanításakor lesznek megsemmisítve.

A kérésenkénti állapotok üzenetkezelőkkel való megosztásához használja az alábbi módszerek egyikét:

Polly-alapú kezelők használata

IHttpClientFactory integrálható a harmadik féltől származó Polly könyvtárral. A Polly egy átfogó rugalmassági és átmeneti hibakezelési kódtár a .NET-hez. Lehetővé teszi, hogy a fejlesztők folyékonyan és szálbiztosan fejezzék ki az olyan szabályzatokat, mint az Újrapróbálkozás, a Megszakító, az Időtúllépés, a Válaszfal elkülönítése és a Fallback.

A bővítménymetelyek lehetővé teszik a Polly-szabályzatok használatát konfigurált HttpClient példányokkal. A Polly-bővítmények támogatják a Polly-alapú kezelők ügyfelekhez való hozzáadását. A Pollyhoz a Microsoft.Extensions.Http.Polly NuGet csomag szükséges.

Átmeneti hibák kezelése

A hibák általában akkor fordulnak elő, ha a külső HTTP-hívások átmenetiek. AddTransientHttpErrorPolicy lehetővé teszi, hogy egy szabályzat definiálva legyen az átmeneti hibák kezelésére. A következő válaszok kezelésére konfigurált AddTransientHttpErrorPolicy szabályzatok:

AddTransientHttpErrorPolicy hozzáférést biztosít egy PolicyBuilder olyan objektumhoz, amely egy lehetséges átmeneti hibát jelképező hibák kezelésére van konfigurálva:

public void ConfigureServices(IServiceCollection services)
{           
    services.AddHttpClient<UnreliableEndpointCallerService>()
        .AddTransientHttpErrorPolicy(p => 
            p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));

    // Remaining code deleted for brevity.

Az előző kódban egy WaitAndRetryAsync szabályzat van definiálva. A sikertelen kérelmeket a rendszer akár háromszor is újrapróbálkozza a kísérletek között 600 ms késleltetéssel.

Szabályzatok dinamikus kiválasztása

A kiterjesztési metódusok például AddPolicyHandler Polly alapú kezelők hozzáadására szolgálnak. A következő AddPolicyHandler túlterhelési metódus (vagy függvény) vizsgálja meg a kérelmet annak eldöntésére, hogy melyik szabályzatot alkalmazza.

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

Az előző kódban, ha a kimenő kérés HTTP GET, a rendszer 10 másodperces időtúllépést alkalmaz. Bármely más HTTP-metódus esetében 30 másodperces időtúllépést használunk.

Több Polly-kezelő hozzáadása

Gyakori a Polly-szabályok beágyazása.

services.AddHttpClient("multiplepolicies")
    .AddTransientHttpErrorPolicy(p => p.RetryAsync(3))
    .AddTransientHttpErrorPolicy(
        p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));

Az előző példában:

  • A rendszer két kezelőt ad hozzá.
  • Az első kezelő AddTransientHttpErrorPolicy segítségével ad hozzá egy újrapróbálkozási szabályzatot. A sikertelen kérelmeket a rendszer akár háromszor is újrapróbálkozza.
  • A második AddTransientHttpErrorPolicy hívás egy áramkör-megszakító házirendet ad hozzá. További külső kérések 30 másodpercig le lesznek tiltva, ha 5 sikertelen kísérlet egymás után történik. Az áramkör-megszakító házirendjei állapotalapúak. Az ügyfélen keresztüli összes hívás ugyanazt az áramkör állapotot osztja meg.

Szabályzatok hozzáadása a Polly-beállításjegyzékből

A rendszeresen használt szabályzatok kezelésének egyik módszere az, hogy egyszeri definiálásuk után regisztrálja azokat egy PolicyRegistry.

A következő kódban:

  • A "normál" és a "hosszú" szabályzat hozzáadásra került.
  • AddPolicyHandlerFromRegistry hozzáadja a "normál" és a "hosszú" szabályzatokat a beállításjegyzékhez.
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.

További információért a IHttpClientFactory és a Polly-integrációkról, lásd a Polly wiki oldalt.

HttpClient és élettartam-kezelése

Minden alkalommal egy új HttpClient példányt ad vissza, amikor a CreateClient-n meghívják a IHttpClientFactory-t. Nevesített ügyfelenként jön létre egy HttpMessageHandler. A gyár kezeli az HttpMessageHandler példányok élettartamát.

IHttpClientFactory összegyűjti a gyár által létrehozott HttpMessageHandler példányokat az erőforrás-felhasználás csökkentése érdekében. A HttpMessageHandler példányok újra felhasználhatók a készletből egy új HttpClient példány létrehozásakor, ha az élettartama még nem járt le.

A kezelők készletezése kívánatos, mivel az egyes kezelők általában saját mögöttes HTTP-kapcsolatokat kezelnek. Ha a szükségesnél több kezelőt hoz létre, az a kapcsolat késéséhez vezethet. Egyes kezelők emellett határozatlan ideig nyitva tartják a kapcsolatokat, ami megakadályozhatja, hogy a kezelő reagáljon a DNS (tartománynévrendszer) változásaira.

Az alapértelmezett kezelő élettartama két perc. Az alapértelmezett érték felülírható nevesített ügyfél alapján:

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

    // Remaining code deleted for brevity.

HttpClient a példányok általában olyan .NET-objektumokként kezelhetők, amelyek nem igényelnek ártalmatlanítást. A felszabadítás megszakítja a kimenő kérelmeket, és garantálja, hogy az adott HttpClient példány nem használható a hívás után Dispose. IHttpClientFactory nyomon követi és megsemmisíti a HttpClient példányok által használt erőforrásokat.

Egyetlen HttpClient példány hosszú ideig történő életben tartása IHttpClientFactory bevezetése előtti gyakori minta. Ez a minta szükségtelenné válik az IHttpClientFactory rendszerre történő migrálás után.

Az IHttpClientFactory alternatívái

A DI-kompatibilis alkalmazások használata IHttpClientFactory elkerüli a következőt:

  • Erőforrás-kimerülési problémák az HttpMessageHandler példányok összevonásával.
  • Régi DNS-problémákat orvosolhatunk az HttpMessageHandler példányok rendszeres időközönkénti újraindításával.

Az előző problémákat többféleképpen is meg lehet oldani egy hosszú élettartamú SocketsHttpHandler példány használatával.

  • Hozzon létre egy SocketsHttpHandler példányt, amikor az alkalmazás elindul, és használja azt az alkalmazás teljes élettartama alatt.
  • Konfiguráljon PooledConnectionLifetime egy megfelelő értékre a DNS frissítési ideje alapján.
  • Hozzon létre HttpClient példányokat new HttpClient(handler, disposeHandler: false) szükség szerint.

Az előző megközelítések hasonló módon oldják meg azokat az erőforrás-kezelési problémákat, amelyeket a IHttpClientFactory megold.

  • A SocketsHttpHandler megosztja a kapcsolatokat a HttpClient példányok között. Ez a megosztás megakadályozza a foglalatok kimerülését.
  • A SocketsHttpHandler a kapcsolatokat PooledConnectionLifetime szerint ciklusokba rendezi az elavult DNS-problémák elkerülése érdekében.

Cookies

A készletezett HttpMessageHandler példányoknál CookieContainer objektumok megosztása történik. A nem várt CookieContainer objektummegosztás gyakran helytelen kódot eredményez. A cookie-kat igénylő alkalmazások esetében fontolja meg az alábbiakat:

  • Az automatikus cookie kezelés letiltása
  • Elkerülve IHttpClientFactory

Hívás ConfigurePrimaryHttpMessageHandler az automatikus cookie kezelés letiltására:

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

Logging

Az ügyfelek, akik a IHttpClientFactory segítségével lettek létrehozva, naplóüzeneteket rögzítenek az összes kéréshez. A naplózási konfigurációban engedélyezze a megfelelő információs szintet az alapértelmezett naplóüzenetek megtekintéséhez. A további naplózás, például a kérelemfejlécek naplózása csak nyomkövetési szinten szerepel.

Az egyes ügyfelekhez használt naplókategória tartalmazza az ügyfél nevét. Egy MyNamedClient nevű ügyfél például naplózza a "System.Net.Http.HttpClient" kategóriával rendelkező üzeneteket. MyNamedClient. LogicalHandler". A LogicalHandlerrel utótaggal ellátott üzenetek a kérelemkezelő folyamaton kívülre kerülnek. A kéréskor a rendszer az üzeneteket naplózza, mielőtt a folyamat többi kezelője feldolgozta volna. A válaszban az üzenetek naplózva lesznek, miután bármely más folyamatkezelő megkapta a választ.

A naplózás a kérelemkezelő folyamaton belül is megtörténik. A MyNamedClient példában ezek az üzenetek a "System.Net.Http.HttpClient" naplókategória szerint vannak naplózva. MyNamedClient. ClientHandler". A kérés esetében ez az összes többi kezelő lefutása után, és közvetlenül a kérés elküldése előtt fordul elő. A válaszban ez a naplózás a válasz állapotát is tartalmazza, mielőtt az áthalad a kezelőfolyamaton.

A folyamaton kívüli és a folyamaton belüli naplózás engedélyezése lehetővé teszi a többi folyamatkezelő által végrehajtott módosítások ellenőrzését. Ez a kérelemfejlécek vagy a válaszállapot-kód módosításait is tartalmazhatja.

Az ügyfél nevének beleszámításával a naplókategória lehetővé teszi a naplószűrést adott nevesített ügyfelek esetében.

A HttpMessageHandler konfigurálása

Szükség lehet az ügyfél által használt belső HttpMessageHandler konfiguráció szabályozására.

A rendszer névvel ellátott vagy beírt ügyfelek hozzáadásakor ad vissza egy IHttpClientBuilder hibát. A ConfigurePrimaryHttpMessageHandler bővítménymetódus használható meghatalmazott definiálására. A meghatalmazott az ügyfél által használt elsődleges HttpMessageHandler kiszolgáló létrehozásához és konfigurálásához használható:

public void ConfigureServices(IServiceCollection services)
{            
    services.AddHttpClient("configured-inner-handler")
        .ConfigurePrimaryHttpMessageHandler(() =>
        {
            return new HttpClientHandler()
            {
                AllowAutoRedirect = false,
                UseDefaultCredentials = true
            };
        });

    // Remaining code deleted for brevity.

Az IHttpClientFactory használata konzolalkalmazásban

Egy konzolalkalmazásban adja hozzá a következő csomaghivatkozásokat a projekthez:

Az alábbi példában:

  • IHttpClientFactory regisztrálva van a Generic Host szolgáltatáskonténerében.
  • MyService létrehoz egy ügyfél-előállító példányt a szolgáltatás alapján, amelyet egy HttpClient létrehozásához használnak. HttpClient weblap lekérésére szolgál.
  • Main létrehoz egy hatókört a szolgáltatás metódusának GetPage végrehajtásához, és írja a weblap tartalmának első 500 karakterét a konzolra.
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}";
            }
        }
    }
}

Élőfej propagálása köztes szoftver

A fejlécpropagálás egy ASP.NET Core köztes szoftver, amely a HTTP-fejléceket propagálja a bejövő kérésből a kimenő HTTP-ügyfélkérelmekbe. A fejlécpropagálás használata:

  • Hivatkozzon a Microsoft.AspNetCore.HeaderPropagation csomagra .

  • Konfigurálja a köztes szoftvert és HttpClient a(z) Startup sorban.

    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();
        });
    }
    
  • Az ügyfél tartalmazza a konfigurált fejléceket a kimenő kérelmekhez:

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

További erőforrások

Glenn Condron, Ryan Nowak és Steve Gordon

Egy IHttpClientFactory regisztrálható és használható HttpClient példányok konfigurálására és létrehozására egy alkalmazásban. A következő előnyöket nyújtja:

  • Központi helyet biztosít a logikai HttpClient-példányok elnevezéséhez és konfigurálásához. Egy GitHub-ügyfél például regisztrálható és konfigurálható a GitHub eléréséhez. Az alapértelmezett ügyfél más célokra is regisztrálható.
  • Kodifikálja a kimenő köztes szoftver fogalmát a kezelők HttpClient delegálásán keresztül, és bővítményeket biztosít a Polly-alapú köztes szoftverhez, hogy kihasználhassa ezt.
  • Kezeli a mögöttes HttpClientMessageHandler példányok készletezését és élettartamát, hogy elkerülje az HttpClient élettartamának manuális kezelésekor előforduló gyakori DNS-problémákat.
  • Konfigurálható naplózási felületet (via ILogger) ad hozzá a gyár által létrehozott ügyfeleken keresztül küldött összes kéréshez.

Mintakód megtekintése vagy letöltése (hogyan töltsd le)

Prerequisites

A .NET-keretrendszert célzó projektekhez telepíteni kell a Microsoft.Extensions.Http NuGet csomagot. A .NET Core-t célzó és a Microsoft.AspNetCore.App metapackage-ra hivatkozó projektek már tartalmazzák a Microsoft.Extensions.Http csomagot.

Használati minták

Az alkalmazásokban többféleképpen IHttpClientFactory is használható:

Egyik sem szigorúan jobb a másiknál. A legjobb megközelítés az alkalmazás korlátaitól függ.

Alapszintű használat

A IHttpClientFactory regisztrálható úgy, hogy a AddHttpClient metóduson belüli IServiceCollection-ra meghívjuk a Startup.ConfigureServices bővítménymetódust.

services.AddHttpClient();

Regisztráció után a kód bárhol elfogadhat egy IHttpClientFactory-t, ahol szolgáltatásokat lehet injektálni függőséginjektálással (DI). IHttpClientFactory használható egy HttpClient példány létrehozásához.

public class BasicUsageModel : PageModel
{
    private readonly IHttpClientFactory _clientFactory;

    public IEnumerable<GitHubBranch> Branches { get; private set; }

    public bool GetBranchesError { get; private set; }

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

    public async Task OnGet()
    {
        var request = new HttpRequestMessage(HttpMethod.Get, 
            "https://api.github.com/repos/dotnet/AspNetCore.Docs/branches");
        request.Headers.Add("Accept", "application/vnd.github.v3+json");
        request.Headers.Add("User-Agent", "HttpClientFactory-Sample");

        var client = _clientFactory.CreateClient();

        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            Branches = await response.Content
                .ReadAsAsync<IEnumerable<GitHubBranch>>();
        }
        else
        {
            GetBranchesError = true;
            Branches = Array.Empty<GitHubBranch>();
        }                               
    }
}

Az ilyen módon történő használat IHttpClientFactory jó módszer egy meglévő alkalmazás átalakítására. Nincs hatással a használat módjára HttpClient . Azokon a helyeken, ahol HttpClient példányokat hoznak létre, cserélje le ezeket az előfordulásokat a CreateClient hívására.

Névvel ellátott ügyfelek

Ha egy alkalmazásnak több, különböző konfigurációval rendelkező, eltérő felhasználásra van szüksége HttpClient, akkor az egyik lehetőség a név szerint megnevezett kliensek használata. A névvel ellátott HttpClient konfiguráció a regisztráció során adható meg a következőben 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");
});

Az előző kódban a AddHttpClient kerül meghívásra, biztosítva a github nevet. Az ügyfél alapértelmezett konfigurációt alkalmaz, nevezetesen az alapcímet és a GitHub API használatához szükséges két fejlécet.

Minden alkalommal, amikor CreateClient meghívásra kerül, létrejön egy új HttpClient példány, és a konfigurációs műveletet is meghívja.

Ahhoz, hogy használjuk a megadott ügyfelet, egy sztring paramétert adhatunk át a(z) CreateClient számára. Adja meg a létrehozandó ügyfél nevét:

public class NamedClientModel : PageModel
{
    private readonly IHttpClientFactory _clientFactory;

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

    public bool GetPullRequestsError { get; private set; }

    public bool HasPullRequests => PullRequests.Any();

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

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

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

        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            PullRequests = await response.Content
                .ReadAsAsync<IEnumerable<GitHubPullRequest>>();
        }
        else
        {
            GetPullRequestsError = true;
            PullRequests = Array.Empty<GitHubPullRequest>();
        }
    }
}

Az előző kódban a kérésnek nem kell állomásnevet megadnia. Csak az elérési utat tudja átadni, mivel a rendszer az ügyfélhez konfigurált alapcímet használja.

Beírt ügyfelek

Beírt ügyfelek:

  • A névvel ellátott ügyfelekkel azonos képességeket biztosíthat anélkül, hogy sztringeket kellene használnia kulcsként.
  • Az IntelliSense és a fordító segítséget nyújtanak a kliensek használata során.
  • Adjon meg egyetlen helyet egy adott HttpClienthely konfigurálásához és használatához. Például egyetlen beírt ügyfél használható egyetlen háttérvégponthoz, és beágyazhatja az adott végponttal foglalkozó összes logikát.
  • A DI-vel dolgozhat, és szükség esetén injektálható az alkalmazásába.

A gépelt ügyfél egy HttpClient paramétert fogad el a konstruktorában.

public class GitHubService
{
    public HttpClient Client { get; }

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

        Client = client;
    }

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

        response.EnsureSuccessStatusCode();

        var result = await response.Content
            .ReadAsAsync<IEnumerable<GitHubIssue>>();

        return result;
    }
}

Az előző kódban a konfiguráció át lesz helyezve a beírt ügyfélbe. Az HttpClient objektum nyilvános tulajdonságként van közzétéve. A funkciókat elérhetővé tevő HttpClient API-specifikus metódusok definiálhatók. A GetAspNetDocsIssues metódus beágyazza a GitHub-adattárak legutóbbi nyitott problémáinak lekérdezéséhez és elemzéséhez szükséges kódot.

Tipizált ügyfél regisztrálásához az AddHttpClient általános bővítménymetódus használható a Startup.ConfigureServices tipizált ügyfélosztály megadásával.

services.AddHttpClient<GitHubService>();

A beírt ügyfél átmenetiként van regisztrálva a DI-ben. A beírt ügyfél közvetlenül injektálható és felhasználható:

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

Ha előnyben részesítik, a típusos ügyfél konfigurációja megadható a regisztráció során Startup.ConfigureServices-ban, nem pedig a típusos ügyfél konstruktorában.

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

A típusos ügyfélbe teljesen beágyazható HttpClient. Ahelyett, hogy tulajdonságként való kitétele történne, nyilvános metódusok is megadhatók, amelyek belsőleg meghívják a HttpClient példányt.

public class RepoService
{
    // _httpClient isn't exposed publicly
    private readonly HttpClient _httpClient;

    public RepoService(HttpClient client)
    {
        _httpClient = client;
    }

    public async Task<IEnumerable<string>> GetRepos()
    {
        var response = await _httpClient.GetAsync("aspnet/repos");

        response.EnsureSuccessStatusCode();

        var result = await response.Content
            .ReadAsAsync<IEnumerable<string>>();

        return result;
    }
}

Az előző kódban a rendszer privát mezőként tárolja az HttpClient adatokat. A külső hívásokhoz való minden hozzáférés a GetRepos metóduson keresztül történik.

Létrehozott ügyfelek

IHttpClientFactory más külső kódtárakkal, például a Refittel együtt is használható. A Refit egy REST .NET-kódtár. Az API-kat élő felületekké alakítja REST . Az interfész implementációját a RestService dinamikusan hozza létre, miközben a HttpClient külső HTTP-hívások végrehajtására szolgál.

Egy felület és egy válasz definiálva van, amely a külső API-t és annak válaszát jelöli:

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

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

Egy beírt ügyfél hozzáadható a Refit használatával a megvalósítás létrehozásához:

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

    services.AddMvc();
}

A definiált felület szükség esetén felhasználható a DI és a Refit által biztosított implementációval:

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

Kimenő kérelem köztes szoftvere

HttpClient már rendelkezik a kimenő HTTP-kérésekhez összekapcsolható kezelők delegálásának fogalmával. Ez IHttpClientFactory megkönnyíti az egyes nevesített ügyfelekre alkalmazni kívánt kezelők meghatározását. Támogatja a több kezelő regisztrációját és láncolását egy kimenő kérelem köztesszoftver-folyamat létrehozásához. Ezek a kezelők a kimenő kérés előtt és után is el tudják végezni a munkát. Ez a minta hasonló a ASP.NET Core bejövő köztes szoftverfolyamatához. A minta egy mechanizmust biztosít a HTTP-kérelmekkel kapcsolatos horizontális problémák kezelésére, beleértve a gyorsítótárazást, a hibakezelést, a szerializálást és a naplózást.

Kezelő létrehozásához definiáljon egy osztályt, amely a forrásból DelegatingHandlerszármazik. Bírálja felül a SendAsync kódot végrehajtó metódust, mielőtt átadja a kérést a folyamat következő kezelőjének:

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

Az előző kód egy alapszintű kezelőt határoz meg. Ellenőrzi, hogy szerepel-e X-API-KEY fejléc a kérelemben. Ha a fejléc hiányzik, elkerülheti a HTTP-hívást, és megfelelő választ ad vissza.

A regisztráció során egy vagy több kezelő is hozzáadható a konfigurációhoz.HttpClient Ezt a feladatot kiterjesztési metódusokkal hajtják végre a következőn: IHttpClientBuilder.

services.AddTransient<ValidateHeaderHandler>();

services.AddHttpClient("externalservice", c =>
{
    // Assume this is an "external" service which requires an API KEY
    c.BaseAddress = new Uri("https://localhost:5000/");
})
.AddHttpMessageHandler<ValidateHeaderHandler>();

Az előző kódban a ValidateHeaderHandler be van regisztrálva a DI-hez. A kezelőt a DI-ben átmeneti szolgáltatásként kell regisztrálni, soha nem hatókörként. Ha a kezelő hatókörrel rendelkező szolgáltatásként van regisztrálva, és a kezelő által igénybe vett szolgáltatások eldobhatók:

  • A kezelő szolgáltatásai a kezelő hatókörének kiesése előtt megsemmisíthetők.
  • Az elvetett kezelőszolgáltatások miatt a kezelő meghibásodik.

Regisztrálás után a AddHttpMessageHandler meghívható, átadva a kezelő típust.

Több kezelő is regisztrálható abban a sorrendben, amelyet végre kell hajtaniuk. Minden kezelő burkolja a következő kezelőt, amíg végül a HttpClientHandler végrehajtja a kérést.

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

A kérésenkénti állapotok üzenetkezelőkkel való megosztásához használja az alábbi módszerek egyikét:

  • Az adatokat a kezelőbe a HttpRequestMessage.Properties használatával továbbítsa.
  • Az aktuális kérés eléréséhez használható IHttpContextAccessor .
  • Hozzon létre egy egyéni AsyncLocal tárolóobjektumot az adatok továbbításához.

Polly-alapú kezelők használata

IHttpClientFactory integrálható egy népszerű, harmadik féltől származó, Polly nevű könyvtárral. A Polly egy átfogó rugalmassági és átmeneti hibakezelési kódtár a .NET-hez. Lehetővé teszi, hogy a fejlesztők folyékonyan és szálbiztosan fejezzék ki az olyan szabályzatokat, mint az Újrapróbálkozás, a Megszakító, az Időtúllépés, a Válaszfal elkülönítése és a Fallback.

A bővítménymetelyek lehetővé teszik a Polly-szabályzatok használatát konfigurált HttpClient példányokkal. A Polly-bővítmények:

  • Polly-alapú kezelők ügyfelekhez való hozzáadásának támogatása.
  • A Microsoft.Extensions.Http.Polly NuGet csomag telepítése után használható. A csomag nem szerepel a ASP.NET Core megosztott keretrendszerében.

Átmeneti hibák kezelése

A leggyakoribb hibák akkor fordulnak elő, ha a külső HTTP-hívások átmenetiek. A rendszer egy kényelmes bővítménymetódust is tartalmaz AddTransientHttpErrorPolicy , amely lehetővé teszi, hogy egy szabályzatot definiáljon az átmeneti hibák kezelésére. Az ezzel a bővítménymódszerrel konfigurált szabályzatok kezelik a HttpRequestException, HTTP 5xx válaszokat és HTTP 408 válaszokat.

A AddTransientHttpErrorPolicy bővítmény a Startup.ConfigureServices-en belül használható. A bővítmény hozzáférést biztosít egy PolicyBuilder olyan objektumhoz, amely egy lehetséges átmeneti hibát jelképező hibák kezelésére van konfigurálva:

services.AddHttpClient<UnreliableEndpointCallerService>()
    .AddTransientHttpErrorPolicy(p => 
        p.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));

Az előző kódban egy WaitAndRetryAsync szabályzat van definiálva. A sikertelen kérelmeket a rendszer akár háromszor is újrapróbálkozza a kísérletek között 600 ms késleltetéssel.

Szabályzatok dinamikus kiválasztása

További bővítménymetelyek is léteznek, amelyek Polly-alapú kezelők hozzáadására használhatók. Az egyik ilyen kiterjesztés, AddPolicyHandleramely több túlterheléssel rendelkezik. Egy túlterhelés lehetővé teszi a kérés vizsgálatát, amikor meghatározza, hogy melyik szabályzatot alkalmazza:

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

Az előző kódban, ha a kimenő kérés HTTP GET, a rendszer 10 másodperces időtúllépést alkalmaz. Bármely más HTTP-metódus esetében 30 másodperces időtúllépést használunk.

Több Polly-kezelő hozzáadása

Gyakori, hogy a Polly-irányelveket egymásba ágyazzák a továbbfejlesztett funkciók biztosítása érdekében.

services.AddHttpClient("multiplepolicies")
    .AddTransientHttpErrorPolicy(p => p.RetryAsync(3))
    .AddTransientHttpErrorPolicy(
        p => p.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));

Az előző példában a rendszer két kezelőt ad hozzá. Az első a AddTransientHttpErrorPolicy bővítmény használatával ad hozzá újrapróbálkozási szabályzatot. A sikertelen kérelmeket a rendszer akár háromszor is újrapróbálkozza. A második hívás, amely AddTransientHttpErrorPolicy circuit breaker politikát ad hozzá. További külső kérések 30 másodpercig le lesznek tiltva, ha öt sikertelen kísérlet egymás után történik. Az áramkör-megszakító házirendjei állapotalapúak. Az ügyfélen keresztüli összes hívás ugyanazt az áramkör állapotot osztja meg.

Szabályzatok hozzáadása a Polly-beállításjegyzékből

A rendszeresen használt szabályzatok kezelésének egyik módszere az, hogy egyszeri definiálásuk után regisztrálja azokat egy PolicyRegistry. A rendszer egy bővítménymetódust biztosít, amely lehetővé teszi egy kezelő hozzáadását a beállításjegyzékből származó szabályzat használatával:

var registry = services.AddPolicyRegistry();

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

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

Az előző kódban két irányelv regisztrálódik, amikor a PolicyRegistry hozzáadásra kerül a ServiceCollection-hez. A beállításjegyzékből származó szabályzat használatához a rendszer a AddPolicyHandlerFromRegistry metódust használja, és átadja az alkalmazandó szabályzat nevét.

A Polly-integrációkkal kapcsolatos IHttpClientFactory további információk a Polly wikiben találhatók.

HttpClient és élettartam-kezelése

Minden alkalommal egy új HttpClient példányt ad vissza, amikor a CreateClient-n meghívják a IHttpClientFactory-t. Van egy HttpMessageHandler megnevezett ügyfél. A gyár kezeli az HttpMessageHandler példányok élettartamát.

IHttpClientFactory összegyűjti a gyár által létrehozott HttpMessageHandler példányokat az erőforrás-felhasználás csökkentése érdekében. A HttpMessageHandler példányok újra felhasználhatók a készletből egy új HttpClient példány létrehozásakor, ha az élettartama még nem járt le.

A kezelők készletezése kívánatos, mivel az egyes kezelők általában saját mögöttes HTTP-kapcsolatokat kezelnek. Ha a szükségesnél több kezelőt hoz létre, az a kapcsolat késéséhez vezethet. Egyes kezelők korlátlan ideig nyitva tartják a kapcsolatokat, ami megakadályozhatja, hogy a kezelő reagáljon a DNS-változásokra.

Az alapértelmezett kezelő élettartama két perc. Az alapértelmezett érték felülírható nevesített ügyfél alapján. A felülbíráláshoz hívja meg a SetHandlerLifetime a IHttpClientBuilder objektumon, amely visszaadásra kerül az ügyfél létrehozásakor.

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

Nincs szükség az ügyfél ártalmatlanítására. A felszabadítás megszakítja a kimenő kérelmeket, és garantálja, hogy az adott HttpClient példány nem használható a hívás után Dispose. IHttpClientFactory nyomon követi és megsemmisíti a HttpClient példányok által használt erőforrásokat. A HttpClient példányok általában olyan .NET-objektumokként kezelhetők, amelyek nem igényelnek ártalmatlanítást.

Egyetlen HttpClient példány hosszú ideig történő életben tartása IHttpClientFactory bevezetése előtti gyakori minta. Ez a minta szükségtelenné válik az IHttpClientFactory rendszerre történő migrálás után.

Az IHttpClientFactory alternatívái

A DI-kompatibilis alkalmazások használata IHttpClientFactory elkerüli a következőt:

  • Erőforrás-kimerülési problémák az HttpMessageHandler példányok összevonásával.
  • Régi DNS-problémákat orvosolhatunk az HttpMessageHandler példányok rendszeres időközönkénti újraindításával.

Az előző problémákat többféleképpen is meg lehet oldani egy hosszú élettartamú SocketsHttpHandler példány használatával.

  • Hozzon létre egy SocketsHttpHandler példányt, amikor az alkalmazás elindul, és használja azt az alkalmazás teljes élettartama alatt.
  • Konfiguráljon PooledConnectionLifetime egy megfelelő értékre a DNS frissítési ideje alapján.
  • Hozzon létre HttpClient példányokat new HttpClient(handler, disposeHandler: false) szükség szerint.

Az előző megközelítések hasonló módon oldják meg azokat az erőforrás-kezelési problémákat, amelyeket a IHttpClientFactory megold.

  • A SocketsHttpHandler megosztja a kapcsolatokat a HttpClient példányok között. Ez a megosztás megakadályozza a foglalatok kimerülését.
  • A SocketsHttpHandler a kapcsolatokat PooledConnectionLifetime szerint ciklusokba rendezi az elavult DNS-problémák elkerülése érdekében.

Cookies

A készletezett HttpMessageHandler példányoknál CookieContainer objektumok megosztása történik. A nem várt CookieContainer objektummegosztás gyakran helytelen kódot eredményez. A cookie-kat igénylő alkalmazások esetében fontolja meg az alábbiakat:

  • Az automatikus cookie kezelés letiltása
  • Elkerülve IHttpClientFactory

Hívás ConfigurePrimaryHttpMessageHandler az automatikus cookie kezelés letiltására:

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

Logging

Az ügyfelek, akik a IHttpClientFactory segítségével lettek létrehozva, naplóüzeneteket rögzítenek az összes kéréshez. A naplózási konfigurációban engedélyezze a megfelelő információs szintet az alapértelmezett naplóüzenetek megtekintéséhez. A további naplózás, például a kérelemfejlécek naplózása csak nyomkövetési szinten szerepel.

Az egyes ügyfelekhez használt naplókategória tartalmazza az ügyfél nevét. Egy MyNamedClient nevű ügyfél például naplózza az üzeneteket a következő kategóriával System.Net.Http.HttpClient.MyNamedClient.LogicalHandler: . A LogicalHandlerrel utótaggal ellátott üzenetek a kérelemkezelő folyamaton kívülre kerülnek. A kéréskor a rendszer az üzeneteket naplózza, mielőtt a folyamat többi kezelője feldolgozta volna. A válaszban az üzenetek naplózva lesznek, miután bármely más folyamatkezelő megkapta a választ.

A naplózás a kérelemkezelő folyamaton belül is megtörténik. A MyNamedClient példában ezek az üzenetek a naplókategória System.Net.Http.HttpClient.MyNamedClient.ClientHandlerszerint vannak naplózva. A kérés esetében ez akkor történik, miután az összes többi kezelő végrehajtotta a feladatát, és közvetlenül azelőtt, hogy a kérést elküldenék a hálózatra. A válaszban ez a naplózás a válasz állapotát is tartalmazza, mielőtt az áthalad a kezelőfolyamaton.

A folyamaton kívüli és a folyamaton belüli naplózás engedélyezése lehetővé teszi a többi folyamatkezelő által végrehajtott módosítások ellenőrzését. Ilyen lehet például a kérelemfejlécek vagy a válaszállapot-kód módosítása.

Ha szükséges, a naplókategória tartalmazza az ügyfél nevét, lehetővé teszi a naplószűrést adott nevesített ügyfelek esetében.

A HttpMessageHandler konfigurálása

Szükség lehet az ügyfél által használt belső HttpMessageHandler konfiguráció szabályozására.

A rendszer névvel ellátott vagy beírt ügyfelek hozzáadásakor ad vissza egy IHttpClientBuilder hibát. A ConfigurePrimaryHttpMessageHandler bővítménymetódus használható meghatalmazott definiálására. A meghatalmazott az ügyfél által használt elsődleges HttpMessageHandler kiszolgáló létrehozásához és konfigurálásához használható:

services.AddHttpClient("configured-inner-handler")
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        return new HttpClientHandler()
        {
            AllowAutoRedirect = false,
            UseDefaultCredentials = true
        };
    });

Az IHttpClientFactory használata konzolalkalmazásban

Egy konzolalkalmazásban adja hozzá a következő csomaghivatkozásokat a projekthez:

Az alábbi példában:

  • IHttpClientFactory regisztrálva van a Generic Host szolgáltatáskonténerében.
  • MyService létrehoz egy ügyfél-előállító példányt a szolgáltatás alapján, amelyet egy HttpClient létrehozásához használnak. HttpClient weblap lekérésére szolgál.
  • A szolgáltatás metódusa GetPage a weblap tartalmának első 500 karakterét írja a konzolra. További információ a szolgáltatások meghívásáról Program.Main: Függőséginjektálás a ASP.NET Core-ban.
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}";
            }
        }
    }
}

Élőfej propagálása köztes szoftver

A fejléc propagálása egy közösség által támogatott köztes szoftver, amely a HTTP-fejléceket a bejövő kérésből a kimenő HTTP-ügyfélkérelmekbe továbbítja. A fejlécpropagálás használata:

  • Hivatkozzon a Fejlécpropagation csomag közösség által támogatott portjára. ASP.NET Core 3.1 vagy újabb verziója támogatja a Microsoft.AspNetCore.HeaderPropagationt.

  • Konfigurálja a köztes szoftvert és HttpClient a(z) Startup sorban.

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    
        services.AddHttpClient("MyForwardingClient").AddHeaderPropagation();
        services.AddHeaderPropagation(options =>
        {
            options.Headers.Add("X-TraceId");
        });
    }
    
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }
    
        app.UseHttpsRedirection();
    
        app.UseHeaderPropagation();
    
        app.UseMvc();
    }
    
  • Az ügyfél tartalmazza a konfigurált fejléceket a kimenő kérelmekhez:

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

További erőforrások