Remarque
L’accès à cette page requiert une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page requiert une autorisation. Vous pouvez essayer de modifier des répertoires.
Dans cet article, vous allez apprendre à utiliser l'interface IHttpClientFactory pour créer des types HttpClient avec différents principes de base .NET, tels que 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. est utilisé pour effectuer des requêtes HTTP et gérer les réponses HTTP à partir de ressources web identifiées par un . Le protocole HTTP couvre la grande majorité de tout le trafic Internet.
Avec les principes de développement d’applications modernes encourageant les meilleures pratiques, le sert d’abstraction d’usine qui peut créer des instances avec des configurations personnalisées. IHttpClientFactory a été introduit dans .NET Core 2.1. Les charges de travail courantes basées sur HTTP .NET peuvent tirer parti facilement d’un middleware tiers résilient et de gestion des pannes transitoires.
Avertissement
Si votre application nécessite des cookies, il est recommandé d’éviter d’utiliser . Le regroupement des instances entraîne le partage d’objets . Le partage imprévu peut fuiter des cookies entre des parties non liées de l’application. De plus, lorsque expire, le gestionnaire est recyclé, ce qui signifie que tous les cookies stockés dans son sont perdus. Pour obtenir d’autres méthodes de gestion des clients, consultez Recommandations pour l’utilisation des clients HTTP.
Importante
La gestion de la durée de vie des instances créées par 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 ou des clients de longue durée avec configuration de . 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
Tous les exemples de code source fournis dans cet article nécessitent l'installation du package NuGet. En outre, les exemples de code démontrent l'utilisation de requêtes HTTP pour récupérer des objets utilisateur à partir de l'API libre {JSON} Placeholder.
Lorsque vous appelez l’une des méthodes d’extension , vous ajoutez le et les services associés au . Le type offre les avantages suivants :
- Expose la classe 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 logiques.
- Codifie le concept d’intergiciel (middleware) sortant via la délégation de gestionnaires dans .
- 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 .
- Gère la mise en cache et la durée de vie des instances 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 .
- Ajoute une expérience de journalisation configurable (via ) pour toutes les requêtes envoyées via des clients créés par la fabrique.
Modèles de consommation
Vous pouvez utiliser dans une application de plusieurs façons :
- Utilisation de base
- Clients nommés
- Clients typés
- Clients générés
La meilleure approche dépend des exigences de l’application.
Utilisation de base
Pour enregistrer le , appelez le .
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 en tant que paramètre de constructeur avec DI. Le code suivant utilise pour créer une instance :
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 [];
}
}
L’utilisation de 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 est utilisé. Aux endroits où des instances sont créées dans une application existante, remplacez ces occurrences par des appels à .
Clients nommés
Les clients nommés sont un bon choix dans les cas suivants :
- L’application nécessite de nombreuses utilisations distinctes de .
- De nombreuses instances de ont des configurations différentes.
La configuration d’un nommé peut être spécifiée lors de l’inscription dans :
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 .
- L’adresse de base .
- En-tête de .
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.
Remarque
Le nombre de clients nommés inscrits distincts ne doit pas être illimité, car il peut entraîner l’épuisement des ressources. Par exemple, ne dérivez pas le nom du client d’une entrée non entrante.
Créer un client
Chaque fois que est appelé :
- Une nouvelle instance de est créée.
- L’action de configuration est appelée.
Pour créer un client nommé, passez son nom dans :
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 [];
}
}
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.
- Fournir un emplacement unique pour configurer et interagir avec un 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 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)
{
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 [];
}
}
Dans le code précédent :
- La configuration est définie lorsque le client typé est ajouté à la collection de services.
- Le est défini en tant que variable (champ) à portée de classe et est utilisé avec des API exposées.
Vous pouvez créer des méthodes spécifiques à l’API qui exposent des fonctionnalités de . Par exemple, la méthode encapsule du code pour récupérer des objets spécifiques à l’utilisateur.
Le code suivant appelle 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, inscrit en tant que service temporaire. Cette inscription utilise une méthode de fabrique pour :
- Créez une instance de .
- Créer une instance de en passant l’instance de à son constructeur.
Importante
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 , le type doit avoir un constructeur qui accepte un en tant que paramètre. De plus, le type 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
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 , avec pour faire les appels HTTP externes.
Considérez le type de suivant :
namespace Shared;
public record class Todo(
int UserId,
int Id,
string Title,
bool Completed);
L’exemple suivant s’appuie sur le package NuGet 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 qui retourne une instance de .
- Déclare un attribut 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 typed 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 lorsque cela est nécessaire, avec l'implémentation fournie par DI 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 . prend également en charge d’autres verbes HTTP, notamment :
POSTPUTDELETEPATCH
Pour obtenir la liste complète des verbes HTTP pris en charge, consultez . 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 :
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 :
- Sérialise le paramètre au format JSON à l’aide de . Cela utilise une instance de pour configurer le processus de sérialisation.
- Crée une instance de pour empaqueter le JSON sérialisé pour l’envoi dans le corps de la requête HTTP.
- Appelle pour envoyer le contenu JSON à l’URL spécifiée. Il s’agit d’une URL relative qui est ajoutée à HttpClient.BaseAddress.
- Appelle pour lever une exception si le code d’état de la réponse n’indique pas la réussite.
prend également en charge d’autres types de contenu. Par exemple : et . Pour obtenir la liste complète du contenu pris en charge, consultez .
L’exemple suivant montre une requête HTTP :
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 . La méthode appelle au lieu de .
L’exemple suivant montre une requête HTTP :
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 appelle . Étant donné que les requêtes HTTP ne contiennent généralement aucun corps, la méthode ne fournit pas de surcharge qui accepte une instance de .
Pour en savoir plus sur l’utilisation de différents verbes HTTP avec , consultez .
Gestion de la durée de vie
Une nouvelle instance est retournée à chaque fois que est appelé sur . Une instance est créée par nom de client. La fabrique gère les durées de vie des instances .
met en cache les instances de créées par la fabrique pour réduire la consommation des ressources. Une instance de peut être réutilisée à partir du cache quand vous créez une instance de si sa durée de vie n’a pas expiré.
La mise en cache des gestionnaires est souhaitable, car chaque gestionnaire gère typiquement son propre pool de connexions HTTP sous-jacent. 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 pour chaque client, sur le lors de l’inscription de dans le .
services.AddHttpClient("Named.Client")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
Importante
Les instances créées par sont destinées à être de courte durée.
Le recyclage et la recréation des quand leur durée de vie expire sont essentiels pour garantir que les gestionnaires réagissent aux modifications du DNS. étant lié à une instance de gestionnaire spécifique lors de sa création, de nouvelles instances doivent être demandées en temps opportun pour garantir que le client obtiendra le gestionnaire mis à jour.
La suppression de telles instances créées par l'usine n’entraîne pas d’épuisement du socket, puisque sa suppression ne déclenche pas l’élimination du . effectue le suivi et l’élimination des ressources utilisées pour créer des instances , en particulier les instances , dès que leur durée de vie expire et qu’il n’y a plus de les utilisant.
Le maintien d’une instance unique de en vie pendant une longue durée est un modèle courant qui peut être utilisé comme alternative à . Cependant, ce modèle nécessite une configuration supplémentaire, comme . Vous pouvez utiliser des clients de longue durée avec , ou des clients à courte durée créés par . Pour plus d’informations sur la stratégie à utiliser dans votre application, consultez Recommandations pour l’utilisation des clients HTTP.
Configurer le
Il peut être nécessaire de contrôler la configuration du interne utilisé par un client.
Un est retourné lors de l'ajout de clients nommés ou typés. La méthode d'extension peut être appelée avec le et recevoir un délégué. Le délégué est utilisé pour créer et configurer le principal utilisé par ce client :
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler
{
AllowAutoRedirect = false,
UseDefaultCredentials = true
};
});
La configuration de vous permet de spécifier un proxy pour l’instance 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 :
| Méthode | Descriptif |
|---|---|
| AddHttpMessageHandler | Ajoute un gestionnaire de messages supplémentaire pour un nommé. |
| AddTypedClient | Configure la liaison entre le et le nommé associé à . |
| ConfigureHttpClient | Ajoute un délégué utilisé pour configurer un nommé. |
| ConfigurePrimaryHttpMessageHandler | Configure le principal à partir du conteneur d’injection de dépendances pour un 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 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 à partir du conteneur d’injection de dépendances pour qu’elle soit utilisée comme gestionnaire principal pour un nommé. (.NET 5+ uniquement) |
Utilisation avec
L’implémentation SocketsHttpHandler de HttpMessageHandler a été ajoutée dans .NET Core 2.1, ce qui permet de configurer PooledConnectionLifetime. Ce paramètre est utilisé pour garantir que le gestionnaire réagit aux modifications DNS. L’utilisation de est donc considérée comme une alternative à l’utilisation de . Pour plus d’informations, consultez Instructions relatives à l’utilisation de clients HTTP.
Toutefois, et peut être utilisé ensemble pour améliorer la configuration. En utilisant ces deux API, vous pouvez configurer à la fois à un bas niveau (par exemple, en utilisant pour la sélection dynamique de certificats) et à un haut niveau (par exemple, en tirant parti de l'intégration de l'injection de dépendances (DI) et de plusieurs configurations clientes).
Pour utiliser les deux API :
- Spécifiez
SocketsHttpHandleren tant quePrimaryHandlervia ConfigurePrimaryHttpMessageHandler ou UseSocketsHttpHandler (.NET 5+ uniquement). - Configurez en fonction de l'intervalle auquel vous vous attendez pour la mise à jour du DNS ; par exemple, pour une valeur précédemment définie dans la méthode d’extension .
- (Facultatif) Puisque gérera le regroupement et le recyclage des connexions, le recyclage du gestionnaire au niveau n’est plus nécessaire. Vous pouvez le désactiver en définissant sur .
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 . 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 , et la section Remarques dans la documentation de l’API .
Éviter les clients typés dans les services singleton
Lorsque vous utilisez l’approche de client nommé, est injecté dans les services et des instances sont créées en appelant chaque fois qu’un 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.
Importante
Les clients typés sont censés être de courte durée dans le même sens que les instances créées par (pour plus d’informations, consultez Gestion de la durée de vie de ). Dès qu’une instance de client typé est créée, 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 .
Si vous devez utiliser des instances dans un service singleton, envisagez les options suivantes :
- Utilisez plutôt l’approche de client nommé, en injectant dans le service singleton et en recréant des instances si nécessaire.
- Si vous avez besoin de l’approche de client typé, utilisez avec le configuré comme gestionnaire principal. Pour plus d’informations sur l’utilisation de avec , consultez la section Utilisation de IHttpClientFactory avec SocketsHttpHandler.
Étendues du gestionnaire de messages dans
crée une étendue d’ID distincte pour chaque instance . Les étendues DI sont distinctes des étendues DI d'application (par exemple, l'étendue de requête entrante ASP.NET ou une étendue DI manuelle créée par l'utilisateur), de sorte qu'elles ne partagent pas des 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 d’application, ce qui peut entraîner, par exemple, la réutilisation de la même instance avec les mêmes dépendances d’étendue injectées entre plusieurs requêtes entrantes.
Diagramme montrant deux étendues d’ID d’application et une étendue de gestionnaire de messages distincte
Il est vivement recommandé aux utilisateurs de ne pas mettre en cache les informations liées à l’étendue (comme les données de ) à l’intérieur des instances 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 temporaire distinct et le wrapper autour d’une instance du cache . Pour accéder au gestionnaire, appelez pour tout client nommé inscrit. Dans ce cas, vous devez créer une instance vous-même en utilisant le gestionnaire construit.
Diagramme montrant l’accès aux étendues d’injection de dépendances d’application via un gestionnaire de messages temporaire distinct et IHttpMessageHandlerFactory
L’exemple suivant montre la création d’un avec un 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 prenant en charge l’étendue et remplacer l’inscription par défaut 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.
Évitez de dépendre du gestionnaire principal « factory-default »
Dans cette section, le terme gestionnaire principal « factory-default » fait référence au gestionnaire principal que l’implémentation par défaut (ou plus précisément, l’implémentation par défaut) attribue si n’est pas configurée d’une manière quelconque.
Remarque
Le gestionnaire principal « factory-default » est un détail d’implémentation et susceptible de changer. ÉVITEZ de compter sur une implémentation spécifique utilisée comme réglage par défaut d'usine (par exemple, ).
Il existe des cas dans lesquels vous devez connaître le type spécifique d’un gestionnaire principal, en particulier si vous travaillez sur une bibliothèque de classes. Tout en préservant la configuration de l'utilisateur final, vous pouvez mettre à jour, par exemple, les propriétés spécifiques de , telles que , et . Il peut être tentant de caster le gestionnaire principal en , ce qui a fonctionné lorsque était utilisé comme gestionnaire principal « par défaut d'usine ». Toutefois, comme n’importe quel code qui dépend des détails d’implémentation, une telle solution de contournement est fragile vouée à l’échec.
Au lieu de vous appuyer sur le gestionnaire principal « factory-default », vous pouvez utiliser pour configurer une instance de gestionnaire principal par défaut « au niveau de l’application » :
// 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) { ... }
});
Vous pouvez également vérifier le type de gestionnaire principal et configurer les spécificités telles que les certificats clients uniquement dans les types de prise en charge connus (probablement, et ) :
// --- "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) { ... }
});
Voir aussi
- Problèmes courants d’utilisation de
- 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