IHttpClientFactory avec .NET
Dans cet article, vous allez apprendre à utiliser l’interface IHttpClientFactory
pour créer des types HttpClient
avec différents principes fondamentaux de .NET, comme l’injection de dépendances (DI), la journalisation et la configuration. Le type HttpClient a été introduit dans .NET Framework 4.5, qui a été publié en 2012. En d’autres termes, il existe déjà depuis un moment. HttpClient
est utilisé pour effectuer des requêtes HTTP et gérer les réponses HTTP à partir de ressources web identifiées par un Uri. Le protocole HTTP couvre la grande majorité de tout le trafic Internet.
Avec les principes de développement d’applications modernes à l’origine des meilleures pratiques, le IHttpClientFactory sert d’abstraction d’usine qui peut créer des instances HttpClient
avec des configurations personnalisées. IHttpClientFactory a été introduit dans .NET Core 2.1. Les charges de travail .NET courantes basées sur HTTP peuvent tirer facilement parti des intergiciels tiers de gestion des erreurs temporaires et résilients.
Notes
Si votre application nécessite des cookies, il peut être préférable d’éviter d’utiliser IHttpClientFactory dans votre application. Pour obtenir d’autres méthodes de gestion des clients, consultez Recommandations pour l’utilisation des clients HTTP.
Important
La gestion de la durée de vie des instances HttpClient
créées par IHttpClientFactory
est complètement différente des instances créées manuellement. Les stratégies sont d’utiliser des clients de courte durée créés par IHttpClientFactory
ou des clients de longue durée avec configuration de PooledConnectionLifetime
. Pour plus d’informations, consultez la section Gestion de la durée de vie de HttpClient et Instructions pour l’utilisation de clients HTTP.
Le type IHttpClientFactory
Tous les exemples de code source fournis dans cet article nécessitent l'installation du package Microsoft.Extensions.Http
NuGet. En outre, les exemples de code démontrent l'utilisation de requêtes HTTP GET
pour récupérer des objets utilisateur Todo
à partir de l'API libre {JSON} Placeholder.
Lorsque vous appelez l’une des méthodes d’extension AddHttpClient, vous ajoutez le IHttpClientFactory
et les services associés au IServiceCollection. Le type IHttpClientFactory
offre les avantages suivants :
- Expose la classe
HttpClient
sous la forme d’un type prêt pour l’intégration de données. - Fournit un emplacement central pour le nommage et la configuration d’instance de
HttpClient
logiques. - Codifie le concept d’intergiciel (middleware) sortant via la délégation de gestionnaires dans
HttpClient
. - Fournit des méthodes d’extension pour les intergiciels basés sur Polly afin de tirer parti de la délégation de gestionnaires dans
HttpClient
. - Gère la mise en cache et la durée de vie des instances HttpClientHandler sous-jacentes. La gestion automatique évite les problèmes courants liés au système DNS (Domain Name System) qui se produisent lors de la gestion manuelle des durées de vie
HttpClient
. - Ajoute une expérience de journalisation configurable (via ILogger) pour toutes les requêtes envoyées via des clients créés par la fabrique.
Modèles de consommation
Vous pouvez utiliser IHttpClientFactory
dans une application de plusieurs façons :
La meilleure approche dépend des exigences de l’application.
Utilisation de base
Pour inscrire le IHttpClientFactory
, appelez 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();
La consommation de services peut nécessiter le IHttpClientFactory
en tant que paramètre de constructeur avec DI. Le code suivant utilise IHttpClientFactory
pour créer une instance HttpClient
:
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 [];
}
}
L’utilisation de IHttpClientFactory
comme dans l’exemple précédent est un bon moyen de refactoriser une application existante. Cela n’a aucun impact sur la façon dont HttpClient
est utilisé. Aux endroits où des instances HttpClient
sont créées dans une application existante, remplacez ces occurrences par des appels à CreateClient.
Clients nommés
Les clients nommés sont un bon choix dans les cas suivants :
- L’application nécessite de nombreuses utilisations distinctes de
HttpClient
. - De nombreuses instances de
HttpClient
ont des configurations différentes.
La configuration d’un HttpClient
nommé peut être spécifiée lors de l’inscription dans IServiceCollection
:
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");
});
Dans le code précédent, le client est configuré avec :
- Nom extrait de la configuration sous le
"TodoHttpClientName"
. - L’adresse de base
https://jsonplaceholder.typicode.com/
. - En-tête de
"User-Agent"
.
Vous pouvez utiliser la configuration pour spécifier des noms de clients HTTP, ce qui est utile pour éviter les erreurs de nommage des clients lors de l’ajout et de la création. Dans cet exemple, le fichier appsettings.json est utilisé pour configurer le nom du client HTTP :
{
"TodoHttpClientName": "JsonPlaceholderApi"
}
Il est facile d’étendre cette configuration et de stocker plus de détails sur le fonctionnement de votre client HTTP. Pour plus d’informations, consultez Configuration dans .NET.
Créer un client
Chaque fois que CreateClient est appelé :
- Une nouvelle instance de
HttpClient
est créée. - L’action de configuration est appelée.
Pour créer un client nommé, passez son nom dans 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 [];
}
}
Dans le code précédent, la requête HTTP n’a pas besoin de spécifier un nom d’hôte. Le code peut simplement passer le chemin, car l’adresse de base configurée pour le client est utilisée.
Clients typés
Clients typés :
- Fournissent les mêmes fonctionnalités que les clients nommés, sans qu’il soit nécessaire d’utiliser des chaînes comme clés.
- Fournissez IntelliSense et l’aide du compilateur lors de la consommation de clients.
- Fournissent un emplacement unique pour configurer et interagir avec un
HttpClient
particulier. Par exemple, un client typé unique peut être utilisé :- Pour un point de terminaison principal unique.
- Pour encapsuler toute la logique traitant du point de terminaison.
- Pour travailler avec l’injection de dépendances et injecter là où c’est nécessaire dans l’application.
Un client typé accepte un paramètre HttpClient
dans son constructeur :
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();
}
Dans le code précédent :
- La configuration est définie lorsque le client typé est ajouté à la collection de services.
- Le
HttpClient
est affecté en tant que variable (champ) à étendue de classe, et utilisé avec des API exposées.
Vous pouvez créer des méthodes spécifiques à l’API qui exposent des fonctionnalités de HttpClient
. Par exemple, la méthode GetUserTodosAsync
encapsule du code pour récupérer des objets Todo
spécifiques à l’utilisateur.
Le code suivant appelle AddHttpClient pour inscrire une classe cliente typée :
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");
});
Le client typé est inscrit comme étant transitoire avec injection de dépendances. Dans le code précédent, AddHttpClient
inscrit TodoService
en tant que service temporaire. Cette inscription utilise une méthode de fabrique pour :
- Créez une instance de
HttpClient
. - Créer une instance de
TodoService
en passant l’instance deHttpClient
à son constructeur.
Important
L’utilisation de clients typés dans les services singleton peut être dangereuse. Pour plus d’informations, consultez la section Éviter les clients typés dans les services singleton.
Remarque
Lors de l’enregistrement d’un client typé avec la méthode AddHttpClient<TClient>
, le type TClient
doit avoir un constructeur qui accepte un HttpClient
en tant que paramètre. De plus, le type TClient
ne doit pas être enregistré séparément dans le conteneur DI, car cela entraînerait le remplacement de l’enregistrement précédent par le suivant.
Clients générés
IHttpClientFactory
peut être utilisé en combinaison avec des bibliothèques tierces, comme Refit. Refit est une bibliothèque REST pour .NET. Cela permet des définitions d’API REST déclaratives, en mappant les méthodes d’interface aux points de terminaison. Une implémentation de l’interface est générée dynamiquement par le RestService
, avec HttpClient
pour faire les appels HTTP externes.
Considérez le type de record
suivant :
namespace Shared;
public record class Todo(
int UserId,
int Id,
string Title,
bool Completed);
L’exemple suivant s’appuie sur le package NuGet Refit.HttpClientFactory
et est une interface simple :
using Refit;
using Shared;
namespace GeneratedHttp.Example;
public interface ITodoService
{
[Get("/todos?userId={userId}")]
Task<Todo[]> GetUserTodosAsync(int userId);
}
L’interface C# précédente :
- Définit une méthode nommée
GetUserTodosAsync
qui retourne une instance deTask<Todo[]>
. - Déclare un attribut
Refit.GetAttribute
avec le chemin d’accès et la chaîne de requête à l’API externe.
Vous pouvez ajouter un client typé en utilisant Refit pour générer l’implémentation :
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");
});
L’interface définie peut être utilisée quand c’est nécessaire, avec l’implémentation fournie par l’injection de dépendances et Refit.
Effectuer des requêtes POST, PUT et DELETE
Dans les exemples précédents, toutes les requêtes HTTP utilisent le verbe HTTP GET
. HttpClient
prend également en charge d’autres verbes HTTP, notamment :
POST
PUT
DELETE
PATCH
Pour obtenir la liste complète des verbes HTTP pris en charge, consultez HttpMethod. Pour plus d’informations sur l’envoi de requêtes HTTP, consultez Envoyer une requête à l’aide de HttpClient.
L’exemple suivant montre comment effectuer une requête HTTP POST
:
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();
}
Dans le code précédent, la méthode CreateItemAsync
:
- Sérialise le paramètre
Item
au format JSON à l’aide deSystem.Text.Json
. Cela utilise une instance de JsonSerializerOptions pour configurer le processus de sérialisation. - Crée une instance de StringContent pour empaqueter le JSON sérialisé pour l’envoi dans le corps de la requête HTTP.
- Appelle PostAsync pour envoyer le contenu JSON à l’URL spécifiée. Il s’agit d’une URL relative qui est ajoutée à HttpClient.BaseAddress.
- Appelle EnsureSuccessStatusCode pour lever une exception si le code d’état de la réponse n’indique pas la réussite.
HttpClient
prend également en charge d’autres types de contenu. Par exemple : MultipartContent et StreamContent. Pour obtenir la liste complète du contenu pris en charge, consultez HttpContent.
L’exemple suivant montre une requête HTTP PUT
:
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();
}
Le code précédent est très similaire à l’exemple POST
. La méthode UpdateItemAsync
appelle PutAsync au lieu de PostAsync
.
L’exemple suivant montre une requête HTTP DELETE
:
public async Task DeleteItemAsync(Guid id)
{
using HttpResponseMessage httpResponse =
await httpClient.DeleteAsync($"/api/items/{id}");
httpResponse.EnsureSuccessStatusCode();
}
Dans le code précédent, la méthode DeleteItemAsync
appelle DeleteAsync. Étant donné que les requêtes HTTP DELETE ne contiennent généralement aucun corps, la méthode DeleteAsync
ne fournit pas de surcharge qui accepte une instance de HttpContent
.
Pour en savoir plus sur l’utilisation de différents verbes HTTP avec HttpClient
, consultez HttpClient.
Gestion de la durée de vie HttpClient
Une nouvelle instance HttpClient
est retournée à chaque fois que CreateClient
est appelé sur IHttpClientFactory
. Une instance HttpClientHandler est créée par nom de client. La fabrique gère les durées de vie des instances HttpClientHandler
.
IHttpClientFactory
met en cache les instances de HttpClientHandler
créées par la fabrique pour réduire la consommation des ressources. Une instance de HttpClientHandler
peut être réutilisée à partir du cache quand vous créez une instance de HttpClient
si sa durée de vie n’a pas expiré.
La mise en cache de gestionnaires en pools est souhaitable, car chaque gestionnaire gère habituellement son propre pool de connexions HTTP sous-jacentes. La création de plus de gestionnaires que nécessaire peut entraîner des épuisements de sockets et des délais de connexion. Certains gestionnaires conservent aussi les connexions indéfiniment ouvertes, ce qui peut empêcher le gestionnaire de réagir aux changements du DNS.
La durée de vie par défaut d’un gestionnaire est de deux minutes. Pour remplacer la valeur par défaut, appelez SetHandlerLifetime pour chaque client, sur le IServiceCollection
:
services.AddHttpClient("Named.Client")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
Important
Les instances HttpClient
créées par IHttpClientFactory
sont destinées à être de courte durée.
Le recyclage et la recréation des
HttpMessageHandler
à l’expiration de leur durée de vie sont essentiels pour queIHttpClientFactory
puisse garantir que les gestionnaires réagissent aux modifications DNS.HttpClient
étant lié à une instance de gestionnaire spécifique lors de sa création, de nouvelles instancesHttpClient
doivent être demandées en temps opportun pour garantir que le client obtiendra le gestionnaire mis à jour.La suppression de telles instances de
HttpClient
créées par la fabrique n’entraîne pas d’épuisement du socket, car son d’élimination ne déclenche pas l’élimination duHttpMessageHandler
.IHttpClientFactory
effectue le suivi et l’élimination des ressources utilisées pour créer des instancesHttpClient
, en particulier les instancesHttpMessageHandler
, dès que leur durée de vie expire et qu’il n’y a plus deHttpClient
les utilisant.
Le maintien d’une instance unique de HttpClient
en vie pendant une longue durée est un modèle courant qui peut être utilisé comme alternative à IHttpClientFactory
. Cependant, ce modèle nécessite une configuration supplémentaire, comme PooledConnectionLifetime
. Vous pouvez utiliser des clients de longue durée avec PooledConnectionLifetime
, ou des clients à courte durée créés par IHttpClientFactory
. Pour plus d’informations sur la stratégie à utiliser dans votre application, consultez Recommandations pour l’utilisation des clients HTTP.
Configurer le HttpMessageHandler
Il peut être nécessaire de contrôler la configuration du HttpMessageHandler interne utilisé par un client.
Un IHttpClientBuilder est retourné quand vous ajoutez des clients nommés ou typés. La méthode d’extension ConfigurePrimaryHttpMessageHandler peut être utilisée pour définir un délégué sur le IServiceCollection
. Le délégué est utilisé pour créer et configurer le HttpMessageHandler
principal utilisé par ce client :
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler
{
AllowAutoRedirect = false,
UseDefaultCredentials = true
};
});
La configuration de HttClientHandler
vous permet de spécifier un proxy pour l’instance HttpClient
entre autres propriétés du gestionnaire. Pour plus d’informations, consultez Proxy par client.
Configuration supplémentaire
Il existe plusieurs options de configuration supplémentaires pour contrôler le IHttpClientHandler
:
Méthode | Description |
---|---|
AddHttpMessageHandler | Ajoute un gestionnaire de messages supplémentaire pour un HttpClient nommé. |
AddTypedClient | Configure la liaison entre le TClient et le HttpClient nommé associés à IHttpClientBuilder . |
ConfigureHttpClient | Ajoute un délégué utilisé pour configurer un HttpClient nommé. |
ConfigurePrimaryHttpMessageHandler | Configure le HttpMessageHandler principal à partir du conteneur d’injection de dépendances pour un HttpClient nommé. |
RedactLoggedHeaders | Définit la collection de noms d’en-têtes HTTP pour lesquels les valeurs doivent être modifiées avant la journalisation. |
SetHandlerLifetime | Définit la durée pendant laquelle une instance de HttpMessageHandler peut être réutilisée. Chaque client nommé peut avoir sa propre valeur de durée de vie de gestionnaire configurée. |
UseSocketsHttpHandler | Configure une nouvelle instance ou une instance précédemment ajoutée de SocketsHttpHandler à partir du conteneur d’injection de dépendances pour qu’elle soit utilisée comme gestionnaire principal pour un HttpClient nommé. (Seulement dans .NET 5+) |
Utilisation de IHttpClientFactory avec SocketsHttpHandler
L’implémentation SocketsHttpHandler
de HttpMessageHandler
a été ajoutée dans .NET Core 2.1, ce qui permet à PooledConnectionLifetime
d’être configuré. Ce paramètre est utilisé pour garantir que le gestionnaire réagit aux modifications DNS. L’utilisation de SocketsHttpHandler
est donc considérée comme une alternative à l’utilisation de IHttpClientFactory
. Pour plus d’informations, consultez Instructions relatives à l’utilisation de clients HTTP.
Toutefois, SocketsHttpHandler
et IHttpClientFactory
peuvent être utilisés ensemble pour améliorer la configurabilité. En utilisant ces deux API, vous bénéficiez de la configurabilité à un niveau faible (par exemple, en utilisant LocalCertificateSelectionCallback
pour la sélection dynamique de certificats) et à un niveau élevé (par exemple, en tirant parti de l’intégration de l’injection de dépendances et de plusieurs configurations clientes).
Pour utiliser les deux API :
- Spécifiez
SocketsHttpHandler
commePrimaryHandler
via ConfigurePrimaryHttpMessageHandler, ou UseSocketsHttpHandler (seulement dans .NET 5+). - Configurez SocketsHttpHandler.PooledConnectionLifetime en fonction de l’intervalle auquel vous vous attendez à ce que le DNS soit mis à jour ; par exemple, à une valeur précédemment définie dans
HandlerLifetime
. - (Facultatif) Puisque
SocketsHttpHandler
gérera le regroupement et le recyclage des connexions, le recyclage du gestionnaire au niveauIHttpClientFactory
n’est plus nécessaire. Vous pouvez le désactiver en définissantHandlerLifetime
surTimeout.InfiniteTimeSpan
.
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
Dans l’exemple ci-dessus, 2 minutes ont été choisies arbitrairement à des fins d’illustration, en s’alignant sur une valeur par défaut de HandlerLifetime
. Vous devez choisir la valeur en fonction de la fréquence attendue du DNS ou d’autres modifications apportées au réseau. Pour plus d’informations, veuillez consulter la section comportement DNS dans les directives HttpClient
, et la section Remarques dans la documentation de l’API PooledConnectionLifetime.
Éviter les clients typés dans les services singleton
Lorsque vous utilisez l’approche de client nommé, IHttpClientFactory
est injecté dans les services et des instances HttpClient
sont créées en appelant CreateClient chaque fois qu’un HttpClient
est nécessaire.
Toutefois, avec l’approche de client typé, les clients typés sont des objets temporaires généralement injectés dans les services. Cela peut causer un problème, car un client typé peut être injecté dans un service singleton.
Important
Les clients typés sont censés être de courte durée dans le même sens que les instances HttpClient
créées par IHttpClientFactory
(pour plus d’informations, consultez Gestion de la durée de vie de HttpClient
). Dès qu’une instance de client typé est créée, IHttpClientFactory
n’a aucun contrôle sur celle-ci. Si une instance de client typée est capturée dans un singleton, elle peut l’empêcher de réagir aux modifications DNS, ce qui va à l’encontre de l’un des objectifs de IHttpClientFactory
.
Si vous devez utiliser des instances HttpClient
dans un service singleton, envisagez les options suivantes :
- Utilisez plutôt l’approche de client nommé, en injectant
IHttpClientFactory
dans le service singleton et en recréant des instancesHttpClient
si nécessaire. - Si vous avez besoin de l’approche de client typé, utilisez
SocketsHttpHandler
avec lePooledConnectionLifetime
configuré comme gestionnaire principal. Pour plus d’informations sur l’utilisation deSocketsHttpHandler
avecIHttpClientFactory
, consultez la section Utilisation de IHttpClientFactory avec SocketsHttpHandler.
Étendues du gestionnaire de messages dans IHttpClientFactory
IHttpClientFactory
crée une étendue d’ID distincte pour chaque instance HttpMessageHandler
. Ces étendues d’ID sont distinctes des étendues d’injection de dépendances d’application (par exemple, étendue de requête entrante ASP.NET ou étendue d’injection de dépendances manuelle créée par l’utilisateur), ainsi elles ne partagent pas d’instances de service délimitées. Les étendues du gestionnaire de messages sont liées à la durée de vie du gestionnaire et peuvent survivre aux étendues de l’application, ce qui peut entraîner, par exemple, la réutilisation de la même instance de HttpMessageHandler
avec les mêmes dépendances injectées étendues entre plusieurs requêtes entrantes.
Il est vivement recommandé aux utilisateurs de ne pas mettre en cache les informations liées à l’étendue (comme les données de HttpContext
) à l’intérieur des instances HttpMessageHandler
et d’utiliser les dépendances délimitées avec précaution pour éviter les fuites d’informations sensibles.
Si vous avez besoin d’accéder à une étendue d’injection de dépendances d’application à partir de votre gestionnaire de messages, par exemple, pour l’authentification, vous devez encapsuler une logique prenant en charge l’étendue dans un DelegatingHandler
temporaire distinct et le wrapper autour d’une instance HttpMessageHandler
du cache IHttpClientFactory
. Pour accéder au gestionnaire, appelez IHttpMessageHandlerFactory.CreateHandler pour tout client nommé inscrit. Dans ce cas, vous devez créer une instance HttpClient
vous-même en utilisant le gestionnaire construit.
L’exemple suivant montre la création d’un HttpClient
avec un DelegatingHandler
prenant en charge l’étendue :
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);
Une autre solution de contournement peut utiliser une méthode d’extension pour inscrire un DelegatingHandler
prenant en charge l’étendue et remplacer l’inscription par défaut IHttpClientFactory
par un service temporaire ayant accès à l’étendue d’application actuelle :
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;
}
Pour plus d’informations, consultez l’exemple complet.
Voir aussi
- Problèmes courants d’utilisation de
IHttpClientFactory
- Injection de dépendances dans .NET
- Journalisation dans .NET
- Configuration dans .NET
- IHttpClientFactory
- IHttpMessageHandlerFactory
- HttpClient
- Effectuer des requêtes HTTP avec HttpClient
- Implémenter une nouvelle tentative HTTP avec backoff exponentiel