J’utilise System.Net.Http.HttpClient
pour effectuer certaines communications HTTP côté client. J’ai tout le HTTP dans un endroit, abstraite du rest du code. Dans un cas, je souhaite lire le contenu de la réponse sous forme de stream, mais le consommateur du stream est bien isolé de l’endroit où la communication HTTP a lieu et le stream est ouvert. À la place responsable de la communication HTTP, je HttpClient
de tous les trucs HttpClient
.
Ce test unitaire échouera dans Assert.IsTrue(stream.CanRead)
:
[TestMethod] public async Task DebugStreamedContent() { Stream stream = null; // in real life the consumer of the stream is far away var client = new HttpClient(); client.BaseAddress = new Uri("https://www.google.com/", UriKind.Absolute); using (var request = new HttpRequestMessage(HttpMethod.Get, "/")) using (var response = await client.SendAsync(request)) { response.EnsureSuccessStatusCode(); //here I would return the stream to the caller stream = await response.Content.ReadAsStreamAsync(); } Assert.IsTrue(stream.CanRead); // FAIL if response is disposed so is the stream }
J’essaie généralement de disposer de tout ce qui est IDisposable
le plus tôt possible, mais dans ce cas, le HttpResponseMessage
dispose également du Stream
renvoyé par ReadAsStreamAsync
.
Il semble donc que le code appelant doit connaître et s’approprier le message de réponse ainsi que le stream, ou que je laisse le message de réponse sans être exposé et que le finaliseur s’en charge. Aucune option ne semble juste.
Cette réponse parle de ne pas disposer du HttpClient
. Que diriez-vous de HttpRequestMessage
et / ou HttpResponseMessage
?
Est-ce que je manque quelque chose? J’espère garder le code consommateur ignorant HTTP, mais le fait de laisser tous ces objects non déplacés circule contre une année d’habitude!
Il semble donc que le code appelant doit connaître et s’approprier le message de réponse ainsi que le stream, ou que je laisse le message de réponse sans être exposé et que le finaliseur s’en charge. Aucune option ne semble juste.
Dans ce cas spécifique, il n’y a pas de finaliseurs . Ni HttpResponseMessage
ni HttpRequestMessage
HttpResponseMessage
un finaliseur (et c’est une bonne chose!). Si vous ne disposez pas de l’un d’eux, ils seront récupérés une fois que le GC sera entré en jeu, et le descripteur de leurs stream sous-jacents sera collecté une fois que cela se produira.
Tant que vous utilisez ces objects, ne vous en débarrassez pas. Une fois cela fait, éliminez-les . Au lieu de les envelopper dans une instruction using
, vous pouvez toujours appeler explicitement Dispose
une fois que vous avez terminé. De toute façon, le code consommateur n’a pas besoin de connaître les requêtes http.
Vous pouvez également prendre le stream en tant que paramètre d’entrée afin que l’appelant ait le contrôle total sur le type du stream ainsi que sur son élimination. Et maintenant, vous pouvez également disposer de httpResponse avant que le contrôle ne quitte la méthode.
Ci-dessous la méthode d’extension pour HttpClient
public static async Task HttpDownloadStreamAsync(this HttpClient httpClient, ssortingng url, Stream output) { using (var httpResponse = await httpClient.GetAsync(url).ConfigureAwait(false)) { // Ensures OK status response.EnsureSuccessStatusCode(); // Get response stream var result = await httpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); await result.CopyToAsync(output).ConfigureAwait(false); output.Seek(0L, SeekOrigin.Begin); } }
Traiter avec Dispose dans .NET est à la fois facile et difficile. Pour sûr.
Les stream tirent ce même non-sens … L’élimination de la mémoire tampon supprime-t-elle également automatiquement le stream qu’il a enveloppé? Devrait-il? En tant que consommateur, devrais-je même savoir si c’est le cas?
Quand je traite de ce genre de choses, je respecte certaines règles:
Vous avez donc un HttpClient, un HttpRequestMessage et un HttpResponseMessage. La durée de vie de chacun d’entre eux, ainsi que tout le matériel jetable qu’ils produisent, doit être respectée. Par conséquent, votre stream ne devrait jamais survivre en dehors de la durée de vie indéfinissable de HttpResponseMessage, car vous n’avez pas instancié le stream.
Dans le scénario ci-dessus, mon schéma serait de prétendre que l’obtention de ce stream n’était en réalité qu’une méthode Static.DoGet (uri), et le stream que vous retourneriez DOIT en faire partie. Cela signifie un deuxième stream, avec le stream de HttpResponseMessage. Copier mon nouveau stream (routage via FileStream, MemoryStream ou autre, selon votre situation) … ou quelque chose de similaire. Parce que:
Donc, utilisez des produits jetables comme «catch-and-release»… produisez-les, accrochez-vous des résultats, libérez-les le plus rapidement possible. Et ne confondez pas optimisation pour exactitude, en particulier pour les classes que vous n’avez pas vous-même écrites.