IHttpClientFactory a .NET-tel

Ebben a cikkben megtanulhatja, hogyan hozhat IHttpClientFactory létre HttpClient típusokat különböző .NET-alapokkal, például függőséginjektálással (DI), naplózással és konfigurációval. A HttpClient típust a 2012-ben megjelent .NET-keretrendszer 4.5-ben vezették be. Más szóval, már egy ideje itt van. HttpClient HTTP-kérések készítésére és a http-válaszok kezelésére szolgál a Uriweberőforrások által azonosított . A HTTP protokoll az internetes forgalom túlnyomó többségét teszi ki.

Az ajánlott eljárásokat hajtó modern alkalmazásfejlesztési alapelvek a gyár absztrakciójaként szolgálnak, IHttpClientFactory amely egyéni konfigurációkkal rendelkező példányokat hozhat létre HttpClient . IHttpClientFactory a .NET Core 2.1-ben jelent meg. A gyakori HTTP-alapú .NET-számítási feladatok könnyedén kihasználhatják a rugalmas és átmeneti hibakezelési külső köztes szoftver előnyeit.

Feljegyzés

Ha az alkalmazás cookie-kat igényel, érdemes lehet elkerülni a használatot IHttpClientFactory az alkalmazásban. Az ügyfelek kezelésének alternatív módjaiért tekintse meg a HTTP-ügyfelek használatának irányelveit.

Fontos

A létrehozott IHttpClientFactory példányok élettartam-kezelése HttpClient teljesen eltér a manuálisan létrehozott példányoktól. A stratégiák a beállított vagy hosszú élettartamú ügyfelek által IHttpClientFactory létrehozott rövid élettartamú ügyfeleket használják PooledConnectionLifetime. További információkért tekintse meg a HttpClient élettartam-kezelési szakaszát és a HTTP-ügyfelek használatának irányelveit.

A IHttpClientFactory típus

A cikkben szereplő összes minta forráskód a Microsoft.Extensions.Http NuGet-csomagra támaszkodik. Emellett HTTP-kéréseket GET is intézünk az ingyenes {JSON} helyőrző API-hoz a felhasználói Todo objektumok lekéréséhez.

Amikor meghívja bármelyik AddHttpClient bővítménymetelyt, hozzáadja a IHttpClientFactory kapcsolódó szolgáltatásokat a IServiceCollection. A IHttpClientFactory típus a következő előnyöket kínálja:

  • Az osztályt HttpClient DI-kész típusként teszi elérhetővé.
  • Központi helyet biztosít a logikai HttpClient példányok elnevezéséhez és konfigurálásához.
  • Kodifikálja a kimenő köztes szoftver fogalmát a HttpClientdelegáló kezelőinek használatával.
  • A Polly-alapú köztes szoftver bővítménymetelyeket biztosít a kezelők delegálásának előnyeinek kihasználásához.HttpClient
  • Kezeli a mögöttes HttpClientHandler példányok gyorsítótárazását és élettartamát. Az automatikus felügyelet elkerüli az élettartamok manuális kezelésekor HttpClient 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.

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

A regisztrációhoz hívja meg a IHttpClientFactorykövetkezőt AddHttpClient:

using Shared;
using BasicHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddHttpClient();
builder.Services.AddTransient<TodoService>();

using IHost host = builder.Build();

A szolgáltatások használatához konstruktorparaméterre lehet szükség a IHttpClientFactory DI használatával. A következő kód egy példány létrehozásához HttpClient használIHttpClientFactory:

using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Shared;

namespace BasicHttp.Example;

public sealed class TodoService(
    IHttpClientFactory httpClientFactory,
    ILogger<TodoService> logger)
{
    public async Task<Todo[]> GetUserTodosAsync(int userId)
    {
        // Create the client
        using HttpClient client = httpClientFactory.CreateClient();
        
        try
        {
            // Make HTTP GET request
            // Parse JSON response deserialize into Todo types
            Todo[]? todos = await client.GetFromJsonAsync<Todo[]>(
                $"https://jsonplaceholder.typicode.com/todos?userId={userId}",
                new JsonSerializerOptions(JsonSerializerDefaults.Web));

            return todos ?? [];
        }
        catch (Exception ex)
        {
            logger.LogError("Error getting something fun to say: {Error}", ex);
        }

        return [];
    }
}

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 a példányok egy meglévő alkalmazásban jönnek létre, cserélje le ezeket az előfordulásokat a következő CreateClienthí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ő felhasználási módja HttpClientvan.
  • Sok HttpClient példány különböző konfigurációval rendelkezik.

A névvel ellátott HttpClient konfiguráció a következő regisztráció IServiceCollectionsorán adható meg:

using Shared;
using NamedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

string? httpClientName = builder.Configuration["TodoHttpClientName"];
ArgumentException.ThrowIfNullOrEmpty(httpClientName);

builder.Services.AddHttpClient(
    httpClientName,
    client =>
    {
        // Set the base address of the named client.
        client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");

        // Add a user-agent default request header.
        client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
    });

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

  • A konfigurációból lekért név a "TodoHttpClientName".
  • Az alapcím https://jsonplaceholder.typicode.com/.
  • Egy "User-Agent" fejléc.

A konfigurációval megadhatja a HTTP-ügyfélneveket, ami segít elkerülni az ügyfelek helytelen elnevezését a hozzáadáskor és a létrehozáskor. Ebben a példában a appsettings.json fájl használatával konfigurálja a HTTP-ügyfél nevét:

{
    "TodoHttpClientName": "JsonPlaceholderApi"
}

Egyszerűen bővítheti ezt a konfigurációt, és további részleteket tárolhat arról, hogy hogyan szeretné a HTTP-ügyfél működését. További információ: Konfiguráció a .NET-ben.

Ügyfél létrehozása

Minden alkalommal CreateClient a következőt hívjuk meg:

  • 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 a nevét a következőbe CreateClient:

using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Shared;

namespace NamedHttp.Example;

public sealed class TodoService
{
    private readonly IHttpClientFactory _httpClientFactory = null!;
    private readonly IConfiguration _configuration = null!;
    private readonly ILogger<TodoService> _logger = null!;

    public TodoService(
        IHttpClientFactory httpClientFactory,
        IConfiguration configuration,
        ILogger<TodoService> logger) =>
        (_httpClientFactory, _configuration, _logger) =
            (httpClientFactory, configuration, logger);

    public async Task<Todo[]> GetUserTodosAsync(int userId)
    {
        // Create the client
        string? httpClientName = _configuration["TodoHttpClientName"];
        using HttpClient client = _httpClientFactory.CreateClient(httpClientName ?? "");

        try
        {
            // Make HTTP GET request
            // Parse JSON response deserialize into Todo type
            Todo[]? todos = await client.GetFromJsonAsync<Todo[]>(
                $"todos?userId={userId}",
                new JsonSerializerOptions(JsonSerializerDefaults.Web));

            return todos ?? [];
        }
        catch (Exception ex)
        {
            _logger.LogError("Error getting something fun to say: {Error}", ex);
        }

        return [];
    }
}

Az előző kódban a HTTP-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.
  • Nyújtson segítséget az IntelliSense és a fordító számára az ügyfelek használatakor.
  • Adjon meg egyetlen helyet egy adott HttpClienthely konfigurálásához és használatához. Használhat például egyetlen beírt ügyfelet:
    • Egyetlen háttérvégponthoz.
    • 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 elfogad egy paramétert HttpClient a konstruktorban:

using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Shared;

namespace TypedHttp.Example;

public sealed class TodoService(
    HttpClient httpClient,
    ILogger<TodoService> logger) : IDisposable
{
    public async Task<Todo[]> GetUserTodosAsync(int userId)
    {
        try
        {
            // Make HTTP GET request
            // Parse JSON response deserialize into Todo type
            Todo[]? todos = await httpClient.GetFromJsonAsync<Todo[]>(
                $"todos?userId={userId}",
                new JsonSerializerOptions(JsonSerializerDefaults.Web));

            return todos ?? [];
        }
        catch (Exception ex)
        {
            logger.LogError("Error getting something fun to say: {Error}", ex);
        }

        return [];
    }

    public void Dispose() => httpClient?.Dispose();
}

A fenti kód a következőket végzi el:

  • A konfiguráció akkor van beállítva, amikor a beírt ügyfél hozzá lesz adva a szolgáltatásgyűjteményhez.
  • Ez HttpClient osztályhatókörű változóként (mezőként) van hozzárendelve, és a közzétett API-kkal együtt használatos.

Olyan API-specifikus metódusok hozhatók létre, amelyek elérhetővé HttpClient teszik a funkciókat. A metódus például beágyazza a GetUserTodosAsync kódot a felhasználóspecifikus Todo objektumok lekéréséhez.

A következő kódhívások AddHttpClient egy beírt ügyfélosztály regisztrálásához:

using Shared;
using TypedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddHttpClient<TodoService>(
    client =>
    {
        // Set the base address of the typed client.
        client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");

        // Add a user-agent default request header.
        client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
    });

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 TodoService . 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 példányt TodoService, amely átmegy a példányon a konstruktornak HttpClient .

Fontos

A gépelt ügyfelek használata az önálló szolgáltatásokban veszélyes lehet. További információkért tekintse meg a Beírt ügyfelek elkerülése a singleton services szakaszban található szakaszt.

Feljegyzés

Ha gépelt ügyfelet regisztrál a AddHttpClient<TClient> metódussal, a TClient típusnak olyan konstruktorral kell rendelkeznie, amely elfogadja a paramétert HttpClient . Emellett a TClient típust nem szabad külön regisztrálni a DI-tárolóban.

Elnevezett és beírt ügyfelek

A megnevezett ügyfelek és a gépelt ügyfelek saját erősségeikkel és gyengeségeikkel rendelkeznek. A két ügyféltípus kombinálásával mindkét világból a legjobbat hozhatja ki.

Az elsődleges használati eset a következő: Használja ugyanazt a beírt ügyfelet, de különböző tartományokhoz. Van például egy elsődleges és egy másodlagos szolgáltatása, és pontosan ugyanazt a funkciót teszik elérhetővé. Ez azt jelenti, hogy ugyanazt a beírt ügyfelet használhatja a kérések kiállításához, a válaszok feldolgozásához és a HttpClient hibák kezeléséhez. Pontosan ugyanazt a kódot fogja használni, de különböző konfigurációkkal (például eltérő alapcím, időtúllépés és hitelesítő adatok).

Az alábbi példa ugyanazt TodoService a beírt ügyfelet használja, amely a beírt ügyfelek szakaszban volt látható.

Először regisztrálja a megnevezett és beírt ügyfeleket.

using Shared;
using TypedHttp.Example;
using Microsoft.Extensions.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddHttpClient<TodoService>("primary"
    client =>
    {
        // Configure the primary typed client
        client.BaseAddress = new Uri("https://primary-host-address.com/");
        client.Timeout = TimeSpan.FromSeconds(3);
    });

// Register the same typed client but with different settings
builder.Services.AddHttpClient<TodoService>("secondary"
    client =>
    {
        // Configure the secondary typed client
        client.BaseAddress = new Uri("https://secondary-host-address.com/");
        client.Timeout = TimeSpan.FromSeconds(10);
    });

A fenti kód a következőket végzi el:

  • Az első AddHttpClient hívás regisztrál egy TodoService beírt ügyfelet a primary név alatt. Az alapul szolgáló HttpClient pontok az elsődleges szolgáltatásra mutatnak, és rövid időtúllépéssel rendelkezik.
  • A második AddHttpClient hívás regisztrál egy TodoService beírt ügyfelet a secondary név alatt. A mögöttes HttpClient pontok a másodlagos szolgáltatásra mutatnak, és hosszabb időtúllépéssel járnak.
using IHost host = builder.Build();

// Fetch an IHttpClientFactory instance to create a named client
IHttpClientFactory namedClientFactory =
    host.Services.GetRequiredService<IHttpClientFactory>();

// Fetch an ITypedHttpClientFactory<TodoService> instance to create a named and typed client
ITypedHttpClientFactory<TodoService> typedClientFactory  =
    host.Services.GetRequiredService<ITypedHttpClientFactory<TodoService>>();

// Create a TodoService instance against the primary host
var primaryClient = namedClientFactory.CreateClient("primary");
var todoService = typedClientFactory.CreateClient(primaryClient);

A fenti kód a következőket végzi el:

  • A IHttpClientFactory rendszer lekéri a példányokat a DI-tárolóból, hogy névvel ellátott ügyfeleket hozzon létre a metódusán CreateClient keresztül.
  • A ITypedHttpClientFactory<TodoService> rendszer lekéri a példányt a DI-tárolóból, hogy a metódusán keresztül CreateClient képes legyen begépelt ügyfeleket létrehozni.
    • Ez a CreateClient túlterhelés egy elnevezett HttpClient (a megfelelő konfigurációval rendelkező) paramétert kapott.
    • A létrehozott todoService szolgáltatás az elsődleges szolgáltatás használatára van konfigurálva.

Feljegyzés

A IHttpClientFactory típus a System.Net.Http névtereken belül, míg a típus a ITypedHttpClientFactoryMicrosoft.Extensions.Httpnévtérben található.

Fontos

Használja a implementációs osztályt (az előző példában a TodoService) a típusparaméterként.ITypedHttpClientFactory Még ha absztrakcióval is rendelkezik (például ITodoService interfész), akkor is használnia kell a megvalósítást. Ha véletlenül használja az absztrakciót (ITodoService), akkor amikor meghívja, CreateClient az egy InvalidOperationException.

try
{
    Todo[] todos = await todoService.GetUserTodosAsync(4);
}
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
{
    // The request timed out against the primary host

    // Create a TodoService instance against the secondary host
    var fallbackClient = namedClientFactory.CreateClient("secondary");
    var todoFallbackService = typedClientFactory.CreateClient(fallbackClient);

    // Issue request against the secondary host
    Todo[] todos = await todoFallbackService.GetUserTodosAsync(4);
}

A fenti kód a következőket végzi el:

  • Megpróbál kérést küldeni az elsődleges szolgáltatásra.
  • Ha a kérés túllépi az időkorlátot (3 másodpercnél hosszabb időt vesz igénybe), akkor egy belsőt TimeoutException küldTaskCanceledException.
  • Időtúllépés esetén egy új ügyfél jön létre és használ, amely most a másodlagos szolgáltatást célozza.

Létrehozott ügyfelek

IHttpClientFactory külső kódtárakkal, például a Refittel együtt használható. A Refit egy REST-kódtár a .NET-hez. Lehetővé teszi a deklaratív REST API-definíciókat, a végpontokhoz való leképezési felületi metódusokat. A felület implementációját a külső HTTP-hívások végrehajtásával dinamikusan RestServiceHttpClient hozza létre a rendszer.

Vegye figyelembe a következő record típust:

namespace Shared;

public record class Todo(
    int UserId,
    int Id,
    string Title,
    bool Completed);

Az alábbi példa a Refit.HttpClientFactory NuGet-csomagra támaszkodik, és egy egyszerű felület:

using Refit;
using Shared;

namespace GeneratedHttp.Example;

public interface ITodoService
{
    [Get("/todos?userId={userId}")]
    Task<Todo[]> GetUserTodosAsync(int userId);
}

Az előző C#-felület:

  • Egy példányt visszaadó Task<Todo[]> metódust GetUserTodosAsync definiál.
  • Deklarál egy Refit.GetAttribute attribútumot a külső API elérési útjával és lekérdezési sztringjével.

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

using GeneratedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Refit;
using Shared;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddRefitClient<ITodoService>()
    .ConfigureHttpClient(client =>
    {
        // Set the base address of the named client.
        client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");

        // Add a user-agent default request header.
        client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
    });

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

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: . A HTTP-kérések küldéséről további információt a HttpClient használatával történő kérés küldése című témakörben talál.

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

public async Task CreateItemAsync(Item item)
{
    using StringContent json = new(
        JsonSerializer.Serialize(item, new JsonSerializerOptions(JsonSerializerDefaults.Web)),
        Encoding.UTF8,
        MediaTypeNames.Application.Json);

    using HttpResponseMessage httpResponse =
        await httpClient.PostAsync("/api/items", json);

    httpResponse.EnsureSuccessStatusCode();
}

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

  • Szerializálja a paramétert Item A JSON használatával System.Text.Json. Ez egy példányt JsonSerializerOptions használ a szerializálási folyamat konfigurálásához.
  • 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.
  • Kivételt jelölő hívások EnsureSuccessStatusCode , 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-kérést PUT mutat be:

public async Task UpdateItemAsync(Item item)
{
    using StringContent json = new(
        JsonSerializer.Serialize(item, new JsonSerializerOptions(JsonSerializerDefaults.Web)),
        Encoding.UTF8,
        MediaTypeNames.Application.Json);

    using HttpResponseMessage httpResponse =
        await httpClient.PutAsync($"/api/items/{item.Id}", json);

    httpResponse.EnsureSuccessStatusCode();
}

Az előző kód nagyon hasonlít a POST példához. A UpdateItemAsync metódus hívása PutAsync ahelyett, hogy PostAsync.

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

public async Task DeleteItemAsync(Guid id)
{
    using HttpResponseMessage httpResponse =
        await httpClient.DeleteAsync($"/api/items/{id}");

    httpResponse.EnsureSuccessStatusCode();
}

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

Ha többet szeretne megtudni a különböző HTTP-parancsok HttpClienthasználatáról, tekintse meg a következőt HttpClient:

HttpClient élettartam-kezelés

A rendszer minden alkalommal új HttpClient példányt ad vissza, amikor CreateClient a rendszer meghívja a IHttpClientFactory. Ügyfélnévenként egy HttpClientHandler példány jön létre. A gyár kezeli a példányok élettartamát HttpClientHandler .

IHttpClientFactory gyorsítótárazza a HttpClientHandler gyár által létrehozott példányokat az erőforrás-felhasználás csökkentése érdekében. A HttpClientHandler példányok újra felhasználhatók a gyorsítótárból egy új HttpClient példány létrehozásakor, ha az élettartama nem járt le.

A kezelők gyorsítótárazása kívánatos, mivel az egyes kezelők általában a saját mögöttes HTTP-kapcsolatkészletét kezelik. A szükségesnél több kezelő létrehozása a szoftvercsatornák kimerülését és a csatlakozási késéseket eredményezheti. 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ülbírálásához hívja meg SetHandlerLifetime az egyes ügyfeleket a IServiceCollectionkövetkezőn:

services.AddHttpClient("Named.Client")
    .SetHandlerLifetime(TimeSpan.FromMinutes(5));

Fontos

HttpClienta létrehozott IHttpClientFactory példányok rövid élettartamúak.

  • HttpMessageHandlerA dns-változásokra való reagálás biztosításához IHttpClientFactory elengedhetetlen, hogy a kezelők az élettartamuk lejártakor újra feldolgozhassák azokat. HttpClient létrehozásakor egy adott kezelőpéldányhoz van kötve, ezért az új HttpClient példányokat időben kell kérni annak biztosítása érdekében, hogy az ügyfél megkapja a frissített kezelőt.

  • A gyár által létrehozott ilyen HttpClient példányok megsemmisítése nem vezet aljzatkimerüléshez, mivel az ártalmatlanítása nem váltja ki a HttpMessageHandler. IHttpClientFactorynyomon követi és megsemmisíti a példányok, különösen a HttpMessageHandler példányok létrehozásához HttpClient használt erőforrásokat, amint lejár az élettartamuk, és már nincs HttpClient használatban.

Az egyetlen HttpClient példány hosszú ideig való életben tartása gyakori minta, amely alternatívaként IHttpClientFactory használható, azonban ehhez a mintához további beállításokra van szükség, például .PooledConnectionLifetime Használhatja a hosszú élettartamú ügyfeleket PooledConnectionLifetime, vagy a rövid élettartamú ügyfeleket, amelyeket a következő IHttpClientFactoryhozott létre: . A HTTP-ügyfelek használatának irányelvei című témakörből megtudhatja, hogy melyik stratégiát érdemes használni az alkalmazásban.

Konfigurálja a HttpMessageHandler

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ódussal megadhatja a delegáltat a IServiceCollection. A meghatalmazott az ügyfél által használt elsődleges HttpMessageHandler kiszolgáló létrehozásához és konfigurálásához használható:

.ConfigurePrimaryHttpMessageHandler(() =>
{
    return new HttpClientHandler
    {
        AllowAutoRedirect = false,
        UseDefaultCredentials = true
    };
});

HttClientHandler A konfigurálás lehetővé teszi a példány proxyjának megadását a HttpClient kezelő különböző egyéb tulajdonságai között. További információ: Proxy per client.

További konfiguráció

A következő konfigurációs lehetőségek IHttpClientHandlerközül választhat:

Metódus Leírás
AddHttpMessageHandler Hozzáad egy további üzenetkezelőt egy elnevezetthez HttpClient.
AddTypedClient Konfigurálja a kötést a TClientHttpClientIHttpClientBuilder.
ConfigureHttpClient Hozzáad egy meghatalmazottat, aki egy elnevezett HttpClientkonfigurálásához lesz használva.
ConfigureHttpMessageHandlerBuilder Olyan meghatalmazottat ad hozzá, amely az üzenetkezelők HttpMessageHandlerBuilder névvel történő HttpClientkonfigurálásához használható.
ConfigurePrimaryHttpMessageHandler Konfigurálja az elsődlegest HttpMessageHandler a függőséginjektálási tárolóból egy nevesített HttpClienttárolóhoz.
RedactLoggedHeaders Beállítja azoknak a HTTP-fejlécneveknek a gyűjteményét, amelyek értékeit a naplózás előtt újra ki kell léptetni.
SetHandlerLifetime A példányok újrafelhasználásának időtartamát HttpMessageHandler adja meg. Minden megnevezett ügyfél saját konfigurált kezelői élettartam-értékkel rendelkezhet.

Az IHttpClientFactory és a SocketsHttpHandler együttes használata

A SocketsHttpHandler megvalósítás a HttpMessageHandler .NET Core 2.1-ben lett hozzáadva, amely lehetővé teszi PooledConnectionLifetime a konfigurálást. Ezzel a beállítással biztosítható, hogy a kezelő reagáljon a DNS-változásokra, ezért a használat SocketsHttpHandler alternatívának minősül IHttpClientFactory. További információt a HTTP-ügyfelek használatának irányelvei című témakörben talál.

A konfigurálhatóság azonban SocketsHttpHandlerIHttpClientFactory együtt is használható. Mindkét API használatával kihasználhatja a konfigurálhatóságot alacsony szinten (például dinamikus tanúsítványkiválasztáshoz) LocalCertificateSelectionCallback és magas szinten (például a DI-integráció és több ügyfélkonfiguráció kihasználásával).

Mindkét API használata:

  1. Adja meg SocketsHttpHandler és PrimaryHandler állítsa be annak értékét PooledConnectionLifetime (például egy korábban használt értékre HandlerLifetime).
  2. Ahogy SocketsHttpHandler a kapcsolatkészletezést és az újrahasznosítást is kezeli, akkor már nincs szükség a kezelői újrahasznosításra a IHttpClientFactory szinten. Letilthatja a következő beállítással HandlerLifetimeTimeout.InfiniteTimeSpan: .
services.AddHttpClient(name)
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        return new SocketsHttpHandler()
        {
            PooledConnectionLifetime = TimeSpan.FromMinutes(2)
        };
    })
    .SetHandlerLifetime(Timeout.InfiniteTimeSpan); // Disable rotation, as it is handled by PooledConnectionLifetime

A gépelt ügyfelek elkerülése az önálló szolgáltatásokban

Az elnevezett ügyfél-megközelítés IHttpClientFactory használatakor a rendszer a szolgáltatásokba injektálja a példányokat, és HttpClient minden alkalommal meghívja CreateClient a példányokat, amikor HttpClient szükség van rá.

A gépelt ügyfél megközelítésével azonban a gépelt ügyfelek általában átmeneti objektumok, amelyeket általában a szolgáltatásokba injektálnak. Ez problémát okozhat, mert egy gépelt ügyfél injektálható egy singleton szolgáltatásba.

Fontos

A gépelt ügyfelek várhatóan rövid élettartamúak lesznek, ugyanúgy, mint HttpClient az általuk IHttpClientFactory létrehozott példányok (további információ: HttpClient élettartam-kezelés). Amint létrejön egy beírt ügyfélpéldány, IHttpClientFactory nincs rá vezérlése. Ha egy gépelt ügyfélpéldányt egyetlenton rögzít, az megakadályozhatja, hogy reagáljon a DNS-változásokra, és az egyik célt legyőzze IHttpClientFactory.

Ha példányokat kell használnia HttpClient egy singleton szolgáltatásban, vegye figyelembe a következő lehetőségeket:

  • Ehelyett használja az elnevezett ügyfél megközelítést, injektálva IHttpClientFactory az egyszeri szolgáltatásba, és szükség esetén újra kell dolgoznia HttpClient a példányokat.
  • Ha a beírt ügyfél megközelítést szeretné használni, használja SocketsHttpHandler elsődleges kezelőként konfigurálva PooledConnectionLifetime . A használattal SocketsHttpHandlerIHttpClientFactorykapcsolatos további információkért tekintse meg az IHttpClientFactory és a SocketsHttpHandler együttes használatát ismertető szakaszt.

Üzenetkezelő hatókörök az IHttpClientFactory-ban

IHttpClientFactory minden HttpMessageHandler példányhoz külön DI-hatókört hoz létre. Ezek a DI-hatókörök eltérnek az alkalmazás DI-hatóköreitől (például ASP.NET bejövő kérelem hatókörétől vagy egy felhasználó által létrehozott manuális DI-hatókörtől), így nem osztanak meg hatókörrel rendelkező szolgáltatáspéldányokat. Az Üzenetkezelő hatókörei a kezelő élettartamához vannak kötve, és túlléphetik az alkalmazás hatóköreit, ami például azt eredményezheti, hogy ugyanazt HttpMessageHandler a példányt ugyanazzal az injektált hatókörű függőséggel használja fel több bejövő kérés között.

Két alkalmazás DI-hatókörét és egy külön üzenetkezelő hatókörét bemutató ábra

A felhasználók számára erősen ajánlott , hogy ne gyorsítótárazza a hatókörrel kapcsolatos információkat (például az adatokat HttpContext) a példányokon belül HttpMessageHandler , és körültekintően használja a hatókörrel rendelkező függőségeket a bizalmas információk kiszivárgásának elkerülése érdekében.

Ha az üzenetkezelőtől szeretne hozzáférést kérni egy alkalmazás DI-hatóköréhez, a hitelesítéshez például a hatókör-tudatos logikát egy külön átmenetibe DelegatingHandlerágyazná be, és körbefuttatná egy HttpMessageHandler példány körül a IHttpClientFactory gyorsítótárból. A regisztrált névvel rendelkező ügyfelek kezelői hívásának IHttpMessageHandlerFactory.CreateHandler elérése. Ebben az esetben saját maga hoz létre egy példányt HttpClient a létrehozott kezelővel.

Ábra az alkalmazás DI-hatóköreihez való hozzáférésről egy külön átmeneti üzenetkezelő és az IHttpMessageHandlerFactory segítségével

Az alábbi példa egy hatókörrel rendelkező DelegatingHandlerobjektum HttpClient létrehozását mutatja be:

if (scopeAwareHandlerType != null)
{
    if (!typeof(DelegatingHandler).IsAssignableFrom(scopeAwareHandlerType))
    {
        throw new ArgumentException($"""
            Scope aware HttpHandler {scopeAwareHandlerType.Name} should
            be assignable to DelegatingHandler
            """);
    }

    // Create top-most delegating handler with scoped dependencies
    scopeAwareHandler = (DelegatingHandler)_scopeServiceProvider.GetRequiredService(scopeAwareHandlerType); // should be transient
    if (scopeAwareHandler.InnerHandler != null)
    {
        throw new ArgumentException($"""
            Inner handler of a delegating handler {scopeAwareHandlerType.Name} should be null.
            Scope aware HttpHandler should be registered as Transient.
            """);
    }
}

// Get or create HttpMessageHandler from HttpClientFactory
HttpMessageHandler handler = _httpMessageHandlerFactory.CreateHandler(name);

if (scopeAwareHandler != null)
{
    scopeAwareHandler.InnerHandler = handler;
    handler = scopeAwareHandler;
}

HttpClient client = new(handler);

Egy további kerülő megoldás egy bővítménymetódussal követve regisztrálhat egy hatóköralapú DelegatingHandler és felülbírált alapértelmezett IHttpClientFactory regisztrációt egy átmeneti szolgáltatásban, amely hozzáfér az aktuális alkalmazás hatóköréhez:

public static IHttpClientBuilder AddScopeAwareHttpHandler<THandler>(
    this IHttpClientBuilder builder) where THandler : DelegatingHandler
{
    builder.Services.TryAddTransient<THandler>();
    if (!builder.Services.Any(sd => sd.ImplementationType == typeof(ScopeAwareHttpClientFactory)))
    {
        // Override default IHttpClientFactory registration
        builder.Services.AddTransient<IHttpClientFactory, ScopeAwareHttpClientFactory>();
    }

    builder.Services.Configure<ScopeAwareHttpClientFactoryOptions>(
        builder.Name, options => options.HttpHandlerType = typeof(THandler));

    return builder;
}

További információt a teljes példában talál.

Lásd még