416 votes

HttpClient et HttpClientHandler doivent-ils être éliminés entre les requêtes ?

System.Net.Http.HttpClient y System.Net.Http.HttpClientHandler dans .NET Framework 4.5 mettent en œuvre IDisposable (via System.Net.Http.HttpMessageInvoker ).

El using La documentation de la déclaration dit :

En règle générale, lorsque vous utilisez un objet IDisposable, vous devez le déclarer et l'instancier dans une instruction using. l'instancier dans une instruction using.

Cette réponse utilise ce modèle :

var baseAddress = new Uri("http://example.com");
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("foo", "bar"),
        new KeyValuePair<string, string>("baz", "bazinga"),
    });
    cookieContainer.Add(baseAddress, new Cookie("CookieName", "cookie_value"));
    var result = client.PostAsync("/test", content).Result;
    result.EnsureSuccessStatusCode();
}

Mais les exemples les plus visibles de Microsoft n'appellent pas à l'aide. Dispose() soit explicitement, soit implicitement. Par exemple :

Dans le annonce quelqu'un a demandé à l'employé de Microsoft :

Après avoir vérifié vos échantillons, j'ai vu que vous n'avez pas effectué l'action "dispose" sur l'instance HttpClient. sur l'instance HttpClient. J'ai utilisé toutes les instances de HttpClient avec l'instruction using dans mon application et j'ai pensé que c'était la bonne méthode puisque HttpClient implémente l'interface IDisposable. Suis-je sur la bon chemin ?

Sa réponse était :

En général, c'est correct, bien qu'il faille faire attention avec avec "using" et async, car ils ne se mélangent pas vraiment dans .Net 4. pouvez utiliser "await" dans une déclaration "using".

En fait, vous pouvez réutiliser le même HttpClient autant de fois que vous le souhaitez. généralement, vous ne les créerez pas et ne les jetterez pas tout le temps.

Le deuxième paragraphe est superflu pour cette question, qui ne concerne pas le nombre de fois que vous pouvez utiliser une instance de HttpClient, mais la question de savoir s'il est nécessaire de s'en débarrasser lorsque vous n'en avez plus besoin.

(Mise à jour : en fait, ce deuxième paragraphe est la clé de la réponse, comme indiqué ci-dessous par @DPeden).

Mes questions sont donc les suivantes :

  1. Est-il nécessaire, compte tenu de l'implémentation actuelle (.NET Framework 4.5), d'appeler Dispose() sur les instances HttpClient et HttpClientHandler ? Clarification : par "nécessaire", je veux dire s'il y a des conséquences négatives à ne pas disposer, comme des risques de fuite de ressources ou de corruption de données.

  2. Si ce n'est pas nécessaire, serait-ce de toute façon une "bonne pratique", puisqu'ils implémentent IDisposable ?

  3. Si c'est nécessaire (ou recommandé), est-ce que ce code mentionné ci-dessus en le mettant en œuvre en toute sécurité (pour .NET Framework 4.5) ?

  4. Si ces classes ne nécessitent pas d'appeler Dispose(), pourquoi ont-elles été implémentées comme IDisposable ?

  5. S'ils l'exigent, ou s'il s'agit d'une pratique recommandée, les exemples de Microsoft sont-ils trompeurs ou dangereux ?

0 votes

"Si ce n'est pas absolument nécessaire..." - c'est jamais Il n'est pas "absolument nécessaire" de se débarrasser de tout ce qui est jetable. De la même manière qu'il n'est pas "absolument nécessaire" de jeter ses déchets de manière responsable si l'on se rend chez quelqu'un d'autre.

2 votes

@Damien_The_Unbeliever, merci pour vos commentaires. Avez-vous des suggestions sur la façon dont je pourrais clarifier la question ? Je veux savoir si cela peut conduire aux problèmes généralement associés à la non-élimination des ressources, tels que la fuite de ressources et la corruption de données.

10 votes

@Damien_The_Unbeliever : C'est faux. En particulier, les écrivains de flux doivent être disposés pour avoir un comportement correct.

302voto

David Peden Points 3532

Le consensus général est que vous n'avez pas (ne devriez pas) avoir besoin de disposer de HttpClient.

C'est ce qu'ont déclaré de nombreuses personnes qui sont intimement liées à son fonctionnement.

Ver Le billet de blog de Darrel Miller et un billet connexe de SO : L'exploration de HttpClient entraîne une fuite de mémoire pour référence.

Je vous suggère aussi fortement de lire le chapitre HttpClient de Concevoir des API Web évolutives avec ASP.NET pour connaître le contexte de ce qui se passe sous le capot, en particulier la section "Cycle de vie" citée ici :

Bien que HttpClient implémente indirectement la fonction IDisposable l'utilisation standard de HttpClient ne consiste 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 éléments suivants comme CredentialCache et CookieContainer à chaque demande, comme cela était comme cela était nécessaire avec HttpWebRequest.

Ou même ouvrir DotPeek.

88 votes

Pour clarifier votre réponse, serait-il correct de dire que "vous n'avez pas besoin de vous débarrasser de HttpClient SI VOUS CONSERVEZ L'INSTANCE POUR LA RÉUTILISER PLUS TARD" ? Par exemple, si une méthode est appelée à plusieurs reprises et crée une nouvelle instance de HttpClient (même si ce n'est pas le modèle recommandé dans la plupart des cas), serait-il encore correct de dire que cette méthode ne doit pas se débarrasser de l'instance (qui ne sera pas réutilisée) ? Cela pourrait conduire à des milliers d'instances non éliminées. En d'autres termes, vous devriez essayer de réutiliser les instances, mais si vous ne les réutilisez pas, vous feriez mieux de les éliminer (pour libérer les connexions) ?

9 votes

Je pense que la réponse correcte, mais frustrante, est que cela dépend. Si je devais me limiter à donner des conseils généraux qui fonctionnent dans la plupart des cas (je ne dis jamais tous), je vous suggérerais d'utiliser un conteneur IoC et d'enregistrer une instance de HttpClient en tant que singleton. La durée de vie de l'instance serait alors adaptée à celle du conteneur. Cela pourrait être fait au niveau de l'application ou peut-être par requête dans une application web.

0 votes

J'en déduis que votre réponse est que le modèle d'utilisation recommandé est d'éviter de créer et d'éliminer de façon répétée les instances de HttpClient, mais de réutiliser la même instance tout au long du cycle de vie de l'application. Ainsi, vous n'en aurez jamais "fini" avec l'instance HttpClient tant que l'application ne sera pas terminée. En suivant ce modèle, vous atteindrez rarement un point où vous aurez "fini" d'utiliser l'instance, jusqu'à la fin de l'application.

19voto

svidgen Points 4012

D'après moi, appeler Dispose() n'est nécessaire que lorsqu'il s'agit de verrouiller des ressources dont vous aurez besoin plus tard (comme une connexion particulière). C'est toujours recommandé de libérer les ressources que vous n'utilisez plus, même si vous n'en avez plus besoin, simplement parce que vous ne devriez pas généralement de conserver des ressources que vous n'utilisez pas (jeu de mots).

L'exemple de Microsoft n'est pas nécessairement incorrect. Toutes les ressources utilisées seront libérées lorsque l'application se terminera. Et dans le cas de cet exemple, cela se produit presque immédiatement après l'exécution de la commande HttpClient a fini d'être utilisé. Dans ce cas, l'appel explicite de Dispose() est quelque peu superflue.

Mais, en général, quand une classe implémente IDisposable il est entendu que vous devez Dispose() de ses instances dès que vous êtes prêt et capable. Je dirais que c'est particulièrement vrai dans les cas tels que HttpClient où il n'est pas explicitement documenté si les ressources ou les connexions sont maintenues/ouvertes. Dans le cas où la connexion sera réutilisée [bientôt], il est préférable de ne pas utiliser la fonction Dipose() Vous n'êtes pas "totalement prêt" dans ce cas.

Voir aussi : Méthode IDisposable.Dispose y Quand appeler Dispose

11 votes

C'est comme si quelqu'un apportait une banane chez vous, la mangeait, et restait là avec la peau. Que doit-il faire avec la peau ? ... S'il s'apprête à partir avec, laissez-le partir. S'il reste dans les parages, il doit la jeter à la poubelle pour éviter qu'elle n'empeste la maison.

0 votes

Pour clarifier cette réponse, êtes-vous en train de dire qu'il n'est pas nécessaire de se débarrasser du programme s'il doit se terminer immédiatement après son utilisation ? Et que vous devriez vous débarrasser du programme s'il est prévu qu'il continue pendant un certain temps à faire d'autres choses ?

0 votes

@FernandoCorreia Oui, à moins que j'oublie quelque chose, je pense que c'est un principe sûr. Mais réfléchissez-y bien dans chaque cas. Si vous travaillez avec une connexion, par exemple, vous ne voulez pas Dispose() d'en sortir prématurément et de devoir se reconnecter quelques secondes plus tard si la connexion existante est réutilisable. De même, vous ne voulez pas inutilement Dispose() d'images ou d'autres structures que vous pourriez être amené à reconstruire en une minute ou deux.

5voto

David Faivre Points 705

Dans mon cas, je créais un HttpClient à l'intérieur d'une méthode qui effectuait l'appel de service. Quelque chose comme :

public void DoServiceCall() {
  var client = new HttpClient();
  await client.PostAsync();
}

Dans un rôle de travailleur Azure, après avoir appelé cette méthode de manière répétée (sans disposer du HttpClient), elle échouerait finalement avec le message suivant SocketException (échec de la tentative de connexion).

J'ai fait du HttpClient une variable d'instance (en le disposant au niveau de la classe) et le problème a disparu. Je dirais donc que oui, disposez du HttpClient, en supposant qu'il est sûr (vous n'avez pas d'appels asynchrones en cours) de le faire.

0 votes

Merci pour les commentaires. Il s'agit d'une question quelque peu complexe. Je vous recommande de lire les articles dont le lien figure dans la réponse de DPeden. En résumé, l'instance de HttpClient doit être réutilisée tout au long du cycle de vie de l'application. Si vous créez de nouvelles instances à plusieurs reprises, vous devrez peut-être les éliminer.

7 votes

"l'instance de HttpClient doit être réutilisée tout au long du cycle de vie de l'application", ce n'est tout simplement pas une bonne idée pour de nombreuses applications. Je pense aux applications web qui utilisent HttpClient. HttpClient détient un état (par exemple les en-têtes de requête qu'il utilisera), de sorte qu'un fil de requête web pourrait facilement piétiner ce qu'un autre est en train de faire. Dans les grandes applications web, j'ai également vu HttpClient être à l'origine de problèmes de connexion majeurs. Dans le doute, je dis Dispose.

1 votes

@nashwan vous ne pouvez pas effacer les en-têtes et en ajouter de nouveaux avant chaque requête ?

3voto

Master T Points 63

Dans une utilisation typique (réponses<2GB), il n'est pas nécessaire de disposer des HttpResponseMessages.

Les types de retour des méthodes HttpClient doivent être éliminés si le contenu de leur flux n'est pas entièrement lu. Sinon, le CLR n'a aucun moyen de savoir que ces flux peuvent être fermés avant la collecte des déchets.

  • Si vous lisez les données dans un byte[] (par exemple avec GetByteArrayAsync) ou une chaîne de caractères, toutes les données sont lues, il n'y a donc pas besoin de disposer.
  • Les autres surcharges lisent par défaut le flux jusqu'à 2 Go (HttpCompletionOption is ResponseContentRead, HttpClient.MaxResponseContentBufferSize default is 2GB).

Si vous avez défini l'option HttpCompletionOption sur ResponseHeadersRead ou si la réponse est supérieure à 2 Go, vous devez procéder à un nettoyage. Cela peut être fait en appelant Dispose sur le HttpResponseMessage ou en appelant Dispose/Close sur le flux obtenu à partir du contenu du HttpResonseMessage ou en lisant complètement le contenu.

Le fait d'appeler Dispose sur le HttpClient dépend de la volonté d'annuler ou non les demandes en attente.

-2voto

yayadavid Points 181

Je pense que l'on devrait utiliser le modèle singleton pour éviter d'avoir à créer des instances de HttpClient et de les fermer tout le temps. Si vous utilisez .Net 4.0, vous pouvez utiliser l'exemple de code ci-dessous. Pour plus d'informations sur le modèle singleton, consultez le site suivant aquí .

class HttpClientSingletonWrapper : HttpClient
{
    private static readonly Lazy<HttpClientSingletonWrapper> Lazy= new Lazy<HttpClientSingletonWrapper>(()=>new HttpClientSingletonWrapper()); 

    public static HttpClientSingletonWrapper Instance {get { return Lazy.Value; }}

    private HttpClientSingletonWrapper()
    {
    }
}

Utilisez le code ci-dessous.

var client = HttpClientSingletonWrapper.Instance;

3 votes

Une chose à laquelle il faut faire attention lorsque l'on réalise ce projet (et d'autres projets similaires) : " Les membres de l'instance ne sont pas garantis d'être thread safe. "

2 votes

Que cette réponse soit correcte ou non dépend totalement de l'application pour laquelle vous souhaitez utiliser HttpClient. Si vous avez une application web et que vous créez un HttpClient singleton à partir duquel toutes les requêtes web seront partagées, vous risquez d'obtenir de nombreuses exceptions de connexion (en fonction de la popularité de votre site web ! :-)). (Voir la réponse de David Faivre)

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