89 votes

HttpClient instance unique avec différents en-têtes d'authentification

Étant donné que le HttpClient de .net a été conçu dans un souci de réutilisation et qu'il est prévu qu'il soit a vécu longtemps y des fuites de mémoire ont été signalées dans des cas de courte durée. Quelles sont les lignes directrices à suivre lorsque vous souhaitez effectuer des appels reposants vers un point de terminaison donné en utilisant différents jetons de support (ou tout en-tête d'autorisation) lorsque vous appelez le point de terminaison pour plusieurs utilisateurs ?

private void CallEndpoint(string resourceId, string bearerToken) {
  httpClient.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("bearer", bearerToken);
  var response = await httpClient.GetAsync($"resource/{resourceid}");
}

Étant donné que le code ci-dessus peut être appelé par un nombre quelconque de threads sur une application web, il est facilement possible que l'en-tête défini dans la première ligne ne soit pas le même que celui qui est utilisé lors de l'appel de la ressource.

Sans causer de contention en utilisant des verrous et en maintenant une application web sans état, quelle est l'approche recommandée pour créer et disposer des HttpClients pour un seul point de terminaison (ma pratique actuelle est de créer un seul client par point de terminaison) ?


Cycle de vie

Bien que HttpClient implémente indirectement l'interface IDisposable l'utilisation recommandée de HttpClient est de ne pas s'en débarrasser après chaque requête. L'objet HttpClient est conçu pour vivre aussi longtemps que aussi longtemps que votre application a besoin d'effectuer des requêtes HTTP. Le fait qu'un objet existe à travers de multiples requêtes permet de définir un endroit pour DefaultRequestHeaders et vous évite d'avoir à respécifier les choses. comme CredentialCache et CookieContainer à chaque requête, comme cela était nécessaire avec HttpWebRequest.

0 votes

Parlez-vous d'un nombre relativement faible d'en-têtes d'authentification différents ou d'un grand nombre, par exemple unique pour chaque utilisateur ?

0 votes

@ToddMenier - Il s'agirait d'un en-tête unique pour chaque utilisateur. Ce serait le jeton oauth de cet utilisateur. Je pense que scott hannen m'a mis sur la bonne voie. Il semble que certaines méthodes d'extension seront en ordre.

0 votes

Bonjour @Bronumski , pouvez-vous partager la façon dont vous avez résolu ce problème ? J'ai le même problème avec plusieurs fils qui ajoutent le même en-tête mais avec un contenu différent.

87voto

Scott Hannen Points 11757

Si vos en-têtes sont généralement les mêmes, vous pouvez définir l'attribut DefaultRequestHeaders . Mais vous n'avez pas besoin d'utiliser cette propriété pour spécifier les en-têtes. Comme vous l'avez déterminé, cela ne fonctionnerait pas si plusieurs threads utilisaient le même client. Les modifications apportées aux en-têtes par défaut sur un thread auraient un impact sur les demandes envoyées sur d'autres threads.

Bien que vous puissiez définir des en-têtes par défaut sur le client et les appliquer à chaque demande, les en-têtes sont en réalité des propriétés de la demande. Ainsi, lorsque les en-têtes sont spécifiques à une demande, il suffit de les ajouter à cette dernière.

request.Headers.Authorization = new AuthenticationHeaderValue("bearer", bearerToken);

Cela signifie que vous ne pouvez pas utiliser les méthodes simplifiées qui n'impliquent pas la création d'un fichier HttpRequest . Vous devrez utiliser

public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)

documenté aquí .


Certains ont trouvé utile d'utiliser des méthodes d'extension pour isoler le code qui met à jour les en-têtes du reste de la méthode.

Exemple de méthodes GET et POST réalisées par le biais d'une méthode d'extension qui vous permet de manipuler l'en-tête de la demande et d'autres éléments de l'interface utilisateur. HttpRequestMessage avant qu'il ne soit envoyé :

public static Task<HttpResponseMessage> GetAsync
    (this HttpClient httpClient, string uri, Action<HttpRequestMessage> preAction)
{
    var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, uri);

    preAction(httpRequestMessage);

    return httpClient.SendAsync(httpRequestMessage);
}

public static Task<HttpResponseMessage> PostAsJsonAsync<T>
    (this HttpClient httpClient, string uri, T value, Action<HttpRequestMessage> preAction)
{
    var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, uri)
    {
        Content = new ObjectContent<T>
            (value, new JsonMediaTypeFormatter(), (MediaTypeHeaderValue)null)
    };
    preAction(httpRequestMessage);

    return httpClient.SendAsync(httpRequestMessage);
}

Ceux-ci pourraient alors être utilisés comme suit :

var response = await httpClient.GetAsync("token",
    x => x.Headers.Authorization = new AuthenticationHeaderValue("basic", clientSecret));

0 votes

Excellente solution. Je viens de l'implémenter. Ajouté un raccourci suivant le modèle ci-dessous public static Task<HttpResponseMessage> GetAsync(this HttpClient httpClient, string uri, string token, CancellationToken cancellationToken) { return httpClient.GetAsync(uri, x => SetToken(x, token), cancellationToken) ; } void SetToken(HttpRequestMessage request, string token, string type = "Bearer")

2 votes

Qu'en est-il HttpClientHandler.Proxy , HttpClientHandler.CookieContainer et d'autres propriétés de HttpClientHandler qui ne peuvent pas être définis dans le HttpRequestMessage ? (ou le peuvent-ils ?)

0 votes

@DavidS. Avez-vous trouvé la solution pour le proxy ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X