Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Ebből a cikkből megtudhatja, hogyan hozhat létre IHttpClientFactory típusú .NET alapokat, például függőséginjektálást (DI), naplózást és konfigurációt a HttpClient felület használatával. A HttpClient típust a 2012-ben kiadott .NET Framework 4.5-ben vezették be. Más szóval, már egy ideje itt van.
HttpClient HTTP-kérések létrehozására és HTTP-válaszok kezelésére szolgál olyan weberőforrásokból, amelyeket Uri azonosít. A HTTP protokoll az internetes forgalom túlnyomó többségét teszi ki.
A modern alkalmazásfejlesztési alapelvek, melyek az ajánlott eljárásokat vezérlik, lehetővé teszik, hogy a IHttpClientFactory gyár absztrakcióként szolgáljon, amely egyéni konfigurációkkal rendelkező HttpClient példányokat hozhat létre.
IHttpClientFactory .NET Core 2.1-ben vezették be. 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.
Figyelmeztetés
Ha az alkalmazás cookie-kat igényel, javasoljuk, hogy kerülje a használatot IHttpClientFactory. Az HttpMessageHandler példányok összesítése az CookieContainer objektumok megosztását eredményezi. A nem várt CookieContainer megosztás cookie-kat szivárogtathat ki az alkalmazás nem kapcsolódó részei között. Továbbá, ha HandlerLifetime lejár, a kezelő újrahasznosításra kerül, ami azt jelenti, hogy a CookieContainer-ben tárolt összes cookie elveszik. Az ügyfelek kezelésének alternatív módjaiért tekintse meg a HTTP-ügyfelek használatának irányelveit.
Fontos
A HttpClient által létrehozott IHttpClientFactory példányok élettartam-kezelése teljesen különbözik a manuálisan létrehozott példányokétól. A stratégiák vagy rövid élettartamú ügyfelek létrehozását jelentik , vagy hosszú élettartamú ügyfelek használatát a IHttpClientFactory beállítással. 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 mintaforráskódhoz telepíteni kell a Microsoft.Extensions.Http NuGet-csomagot. A példakódok emellett azt is bemutatják, hogy a http-kérések GET használatával lekérhetők a felhasználói Todo objektumok az ingyenes {JSON} helyőrző API-ból.
Amikor meghívja bármelyik AddHttpClient bővítménymódszert, hozzáadja a IHttpClientFactory és a kapcsolódó szolgáltatásokat a IServiceCollection. A IHttpClientFactory típus a következő előnyöket kínálja:
- Az osztályt
HttpClientDI-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ők 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 - A mögöttes HttpClientHandler példányok gyorsítótárazását és élettartam-kezelését végzi. Az automatikus felügyelet elkerüli az élettartamok manuális kezelésekor
HttpClientelő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 fel a IHttpClientFactoryAddHttpClient.
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 fogyasztása konstrukciós paraméterként igényelheti a IHttpClientFactoryDI használatát. A következő kód IHttpClientFactory-t használ egy HttpClient példány létrehozásához.
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
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 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
HttpClienthasználatára. - Sok
HttpClientpéldány különböző konfigurációval rendelkezik.
A HttpClient-en történő regisztráció során megadható a névvel ellátott IServiceCollection konfigurációja.
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ó alatt a
"TodoHttpClientName"-ről lekért név. - 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ó: Configuration in .NET.
Feljegyzés
A különböző regisztrált névvel ellátott ügyfelek száma nem lehet korlátlan, mert az erőforrás-kimerüléshez vezethet. Ne származtasd például az ügyfélnevet a kötetlen bemenetből.
Ügyfél létrehozása
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:
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"];
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.
- Biztosítson IntelliSense- és fordítói segítséget az ügyfelek használatához.
- 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.
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)
{
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 [];
}
}
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
HttpClientosztá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ódsor meghívja a AddHttpClient-t egy típusos ü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:
- Hozza létre a
HttpClientegy példányát. - Hozzon létre egy
TodoServicepéldányt, amelynek konstruktorába adja át aHttpClientpéldányt.
Fontos
A típussal rendelkező kliensek használata a szingelton 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 paraméterként elfogad egy HttpClient konstruktort. Emellett a TClient típust nem szabad külön regisztrálni a DI-tárolóban, mivel ez azt eredményezi, hogy a későbbi regisztráció felülírja az előbbit.
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 .NET. Lehetővé teszi a deklaratív REST API-definíciókat, a végpontokhoz való leképezési felületi metódusokat. 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.
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:
- A(z)
GetUserTodosAsyncnevű metódus, amely egyTask<Todo[]>példányt ad vissza. - Deklarál egy
Refit.GetAttributeattribú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 typed 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:
POSTPUTDELETEPATCH
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
Itemparamétert JSON formátumba, aSystem.Text.Jsonhaszná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-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 a PutAsync-t hívja, nem pedig a PostAsync-t.
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 DeleteItemAsync metódus meghívja a DeleteAsync metódust. Mivel a HTTP-kérések DELETE általában nem tartalmaznak törzset, a DeleteAsync metódus nem biztosít olyan túlterhelést, amely elfogadja a HttpContent példányát.
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.
HttpClient élettartam-kezelés
Minden alkalommal egy új HttpClient példányt ad vissza, amikor a CreateClient-n meghívják a IHttpClientFactory-t. Ügyfélnévenként egy HttpClientHandler példány jön létre. A gyár kezeli az HttpClientHandler példányok élettartamát.
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árba helyezése kívánatos, mivel az egyes kezelők általában a saját háttérbeli HTTP-kapcsolatkészletét kezelik. A szükségesnél több kezelő létrehozása a hálózati aljzatok 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 a SetHandlerLifetime minden ügyfél esetében, a IHttpClientBuilder-en a IHttpClientFactoryServiceCollection regisztrálásakor.
services.AddHttpClient("Named.Client")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
Fontos
HttpClient által létrehozott IHttpClientFactory példányok célja, hogy rövid élettartamúak legyenek.
Az élettartamuk lejártakor a
HttpMessageHandlerújrahasznosítása és újraalkotása elengedhetetlenIHttpClientFactoryannak érdekében, hogy a kezelők reagáljanak a DNS-változásokra.HttpClientlétrehozásakor egy adott kezelőpéldányhoz van kötve, ezért az újHttpClientpéldányokat időben kell kérni annak biztosítása érdekében, hogy az ügyfél megkapja a frissített kezelőt.Az ilyen
HttpClientpéldányok megsemmisítése, amelyeket a gyár hozott létre, nem vezet szocketkimerüléshez, mivel a példányok ártalmatlanítása nem váltja ki aHttpMessageHandlerártalmatlanítását.IHttpClientFactorynyomon követi és megsemmisíti az erőforrásokat, amelyeket aHttpClientpéldányok, különösen aHttpMessageHandlerpéldányok létrehozásához használnak, amint ezek a példányok esetében lejár az élettartamuk, és már egyetlenHttpClientsem használja őket.
Az egyetlen HttpClient példány hosszú ideig való életben tartása gyakori minta, amely alternatívájaként használható, azonban ehhez a mintához további beállításokra van szükség, például IHttpClientFactory. Használhatja a hosszú élettartamú ügyfeleket PooledConnectionLifetime, vagy a rövid élettartamú ügyfeleket, amelyeket IHttpClientFactory hozott 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ódus meghívható a IHttpClientBuilder meghatalmazotton, és átadható. 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
};
});
HttpClientHandler konfigurálása lehetővé teszi, hogy megadja a HttpClient példány proxyját a kezelő további különböző tulajdonságai között. További információ: Proxy per client.
További konfiguráció
Számos további konfigurációs lehetőség áll rendelkezésre a IHttpClientBuilder vezérléséhez.
| Metódus | Leírás |
|---|---|
| AddHttpMessageHandler | Hozzáad egy további üzenetkezelőt egy elnevezetthez HttpClient. |
| AddTypedClient | Konfigurálja a kötést a TClient és a megnevezett HttpClient kapcsolódó IHttpClientBuilder között. |
| ConfigureHttpClient | Hozzáad egy delegáltat, amelyet egy elnevezett HttpClient konfigurálására fog használni. |
| ConfigurePrimaryHttpMessageHandler | Konfigurálja az elsődlegest HttpMessageHandler a függőséginjektálási tárolóból egy megnevezett HttpClient számára. |
| 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 HttpMessageHandler példány újrafelhasználásának időtartamát állítja be. Minden megnevezett ügyfél saját konfigurált kezelői élettartam-értékkel rendelkezhet. |
| UseSocketsHttpHandler | Konfigurál egy új vagy egy korábban hozzáadott SocketsHttpHandler példányt a függőséginjektálási tárolóból úgy, hogy a megadott nevű HttpClient példány elsődleges kezelője legyen. (csak .NET 5+) |
A IHttpClientFactory és SocketsHttpHandler együttes használata
A SocketsHttpHandlerHttpMessageHandler implementációja .NET Core 2.1-ben lett hozzáadva, amely lehetővé teszi PooledConnectionLifetime konfigurálását. Ezzel a beállítással biztosítható, hogy a kezelő reagáljon a DNS-változásokra, így az SocketsHttpHandler használata alternatívának tekinthető az IHttpClientFactory használatához képest. További információt a HTTP-ügyfelek használatának irányelvei című témakörben talál.
Azonban együtt SocketsHttpHandlerIHttpClientFactory is használható a konfigurálás javítására. 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:
- Adja meg
SocketsHttpHandler-tPrimaryHandler-ként ConfigurePrimaryHttpMessageHandler-n keresztül, vagy UseSocketsHttpHandler-val (.NET 5+ esetén). - Állítsa be SocketsHttpHandler.PooledConnectionLifetime a DNS frissítésének várható időköze alapján, például a bővítménymetódusban
SetHandlerLifetimekorábban beállított értékre. - (Nem kötelező) Mivel
SocketsHttpHandlerkezelni fogja a kapcsolatkészletezést és az újrahasznosítást, már nincs szükség kezelői újrahasznosításra aIHttpClientFactoryszinten. Letilthatja, ha aHandlerLifetimeértékétTimeout.InfiniteTimeSpan-re állítja be.
services.AddHttpClient(name)
.UseSocketsHttpHandler((handler, _) =>
handler.PooledConnectionLifetime = TimeSpan.FromMinutes(2)) // Recreate connection every 2 minutes
.SetHandlerLifetime(Timeout.InfiniteTimeSpan); // Disable rotation, as it is handled by PooledConnectionLifetime
A fenti példában a rendszer 2 percet választott tetszőlegesen illusztrációs célokra, az alapértelmezett HandlerLifetime értékhez igazítva. Az értéket a DNS vagy más hálózati változások várható gyakorisága alapján kell kiválasztania. További információkért tekintse meg az irányelvek DNS-viselkedési szakaszát HttpClient és az API dokumentációjának Megjegyzések szakaszát PooledConnectionLifetime .
Kerülje a típusos kliensek használatát az egyetlen példányos szolgáltatásokban
Az elnevezett ügyfél-megközelítésnél a a szolgáltatásokba injektálódik, és a IHttpClientFactory példányok a HttpClient meghívásával jönnek létre, valahányszor szükség van CreateClient.
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, mivel egy típizált ü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 az általuk HttpClient (további információért lásd: IHttpClientFactory). Amint létrejön egy típusos ügyfélpéldány, IHttpClientFactory nincs rá irányítása. Ha egy típusosított klienspéldány szinglentonban van rögzítve, az megakadályozhatja, hogy reagáljon a DNS-változásokra, ezzel veszélyeztetve az IHttpClientFactory egyik célját.
Ha HttpClient példányokat kell használnia egy singleton szolgáltatásban, vegye figyelembe a következő lehetőségeket:
- Ehelyett használja az elnevezett ügyfél megközelítést, és injektálja
IHttpClientFactoryaz egyedüli szolgáltatásba, szükség esetén pedig hozza létre újraHttpClientpéldányokat. - Ha a típusos kliens megközelítést szeretné használni, konfigurálja
SocketsHttpHandler-t elsődleges kezelőként aPooledConnectionLifetimehasználatával. A használattalSocketsHttpHandlerIHttpClientFactorykapcsolatos további információkért tekintse meg az IHttpClientFactory és a SocketsHttpHandler együttes használatát ismertető szakaszt.
Üzenetkezelő hatókörei a(z) IHttpClientFactory
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 not megosztják a 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örrel rendelkező függőséggel használja fel több bejövő kérés között.
Felhasználóknak erősen ajánlott nem gyorsítótárazni a hatókörrel kapcsolatos információkat (mint például az adatokat HttpContext) HttpMessageHandler példányokban, és óvatosan használni a hatókörfü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 IHttpMessageHandlerFactory.CreateHandler hívás kezelőjének elérése bármely regisztrált nevesített kliens esetén. Ebben az esetben saját maga hoz létre egy HttpClient példányt a megadott kezelővel.
Az alábbi példa bemutatja egy HttpClient hatókör-érzékeny DelegatingHandler létrehozását.
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);
További kerülő megoldásként használható egy bővítménymetódus, amely lehetővé teszi egy hatókörérzékeny DelegatingHandler regisztrációját és a IHttpClientFactory alapértelmezett regisztráció felülírását egy átmeneti szolgáltatással, 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.
Kerülje a "gyári alapértelmezett" elsődleges kezelő használatát.
Ebben a szakaszban a "gyári alapértelmezett" elsődleges kezelő kifejezés arra az elsődleges kezelőre vonatkozik, amelyet az alapértelmezett IHttpClientFactory implementáció (pontosabban az alapértelmezett HttpMessageHandlerBuilder implementáció) rendel hozzá abban az esetben, ha egyáltalán nincs konfigurálva.
Feljegyzés
A "gyári alapértelmezett" elsődleges kezelőprogram egy implementációs részlet, amely változhat.
❌ AVOID függ egy adott implementációtól, amelyet "gyári alapértelmezettként" használnak (például HttpClientHandler).
Vannak olyan esetek, amikor ismernie kell az elsődleges kezelő konkrét típusát, különösen akkor, ha osztálykönyvtáron dolgozik. A végfelhasználó konfigurációjának megőrzése mellett érdemes lehet frissíteni például HttpClientHandler-specifikus tulajdonságokat, például ClientCertificates, UseCookiesés UseProxy. Kísértést érezhetünk, hogy az elsődleges kezelőt HttpClientHandler típusra alakítsuk, ami éppen akkor működött, amikor HttpClientHandler "gyári alapértelmezett" elsődleges kezelőként volt használatban. Az implementáció részleteitől függően azonban az ilyen áthidaló megoldás törékeny és töréshez kötött.
Ahelyett, hogy a "gyári alapértelmezett" elsődleges kezelőre támaszkodna, a ConfigureHttpClientDefaults segítségével beállíthat egy "alkalmazás-szintű" alapértelmezett elsődleges kezelőpéldányt:
// Contract with the end-user: Only HttpClientHandler is supported.
// --- "Pre-configure" stage ---
// The default is fixed as HttpClientHandler to avoid depending on the "factory-default"
// Primary Handler.
services.ConfigureHttpClientDefaults(b =>
b.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() { UseCookies = false }));
// --- "End-user" stage ---
// IHttpClientBuilder builder = services.AddHttpClient("test", /* ... */);
// ...
// --- "Post-configure" stage ---
// The code can rely on the contract, and cast to HttpClientHandler only.
builder.ConfigurePrimaryHttpMessageHandler((handler, provider) =>
{
if (handler is not HttpClientHandler h)
{
throw new InvalidOperationException("Only HttpClientHandler is supported");
}
h.ClientCertificates.Add(GetClientCert(provider, builder.Name));
//X509Certificate2 GetClientCert(IServiceProvider p, string name) { ... }
});
Másik lehetőségként érdemes lehet ellenőrizni az elsődleges kezelő típusát, és konfigurálni az ügyféltanúsítványokhoz hasonló jellemzőket csak a jól ismert támogató típusok esetében (valószínűleg HttpClientHandler és SocketsHttpHandler):
// --- "End-user" stage ---
// IHttpClientBuilder builder = services.AddHttpClient("test", /* ... */);
// ...
// --- "Post-configure" stage ---
// No contract is in place. Trying to configure main handler types supporting client
// certs, logging and skipping otherwise.
builder.ConfigurePrimaryHttpMessageHandler((handler, provider) =>
{
if (handler is HttpClientHandler h)
{
h.ClientCertificates.Add(GetClientCert(provider, builder.Name));
}
else if (handler is SocketsHttpHandler s)
{
s.SslOptions ??= new System.Net.Security.SslClientAuthenticationOptions();
s.SslOptions.ClientCertificates ??= new X509CertificateCollection();
s.SslOptions.ClientCertificates!.Add(GetClientCert(provider, builder.Name));
}
else
{
// Log warning
}
//X509Certificate2 GetClientCert(IServiceProvider p, string name) { ... }
});