J'utilise System.Net.Http.HttpClient
pour effectuer des communications HTTP côté client. J'ai tout le HTTP en un seul endroit, éloigné du reste du code. Dans un cas, je veux lire le contenu de la réponse sous forme de flux, mais le consommateur du flux est bien isolé de l'endroit où la communication HTTP se produit et le flux est ouvert. A l'endroit responsable de la communication HTTP, je dispose de tout le HttpClient
.
Ce test unitaire échouera à 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 d'éliminer tout ce qui est IDisposable
le plus tôt possible, mais dans ce cas, l'élimination du HttpResponseMessage
supprime également le Stream
renvoyé de ReadAsStreamAsync
.
Il semble donc que le code appelant doive connaître et s'approprier le message de réponse ainsi que le flux, ou je laisse le message de réponse non exposé et laisse le finaliseur s'en occuper. Aucune des deux options ne semble appropriée.
Cette réponse parle de ne pas disposer du HttpClient
. Qu'en est-il des HttpRequestMessage
et/ou HttpResponseMessage
?
Suis-je en train de manquer quelque chose? J'espère garder le code consommateur ignorant de HTTP mais laisser tous ces objets non disposés va à l'encontre de l'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 flux, ou je laisse le message de réponse non exposé et laisse le finaliseur s'en occuper. Aucune des deux options ne semble appropriée.
Dans ce cas spécifique, il n'y a pas de finaliseurs. Ni HttpResponseMessage
ni HttpRequestMessage
n'implémentent un finaliseur (et c'est une bonne chose!). Si vous ne disposez pas de l'un d'eux, ils récupèreront les ordures une fois que le GC entre en jeu, et la poignée de leurs flux sous-jacents sera collectée une fois que cela se produira.
Tant que vous utilisez ces objets, ne les jetez pas. Une fois terminé, jetez-les. Au lieu de les encapsuler dans une instruction using
, vous pouvez toujours appeler explicitement Dispose
une fois que vous avez terminé. Dans tous les cas, le code consommateur n'a pas besoin de connaître les requêtes http sous-jacentes.
Vous pouvez également prendre le flux comme paramètre d'entrée, afin que l'appelant ait un contrôle complet sur le type du flux ainsi que sur son élimination. Et maintenant, vous pouvez également supprimer 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, string 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);
}
}
Gérer les suppressions dans .NET est à la fois facile et difficile. Pour sûr.
Les flux tirent ce même non-sens ... Est-ce que l'élimination du tampon élimine également automatiquement le flux qu'il a enveloppé? Devrait-il? En tant que consommateur, devrais-je même savoir si c'est le cas?
Quand je m'occupe de ce genre de choses, je respecte certaines règles:
Donc, vous avez un HttpClient, un HttpRequestMessage et un HttpResponseMessage. La durée de vie de chacun d'eux, et tout jetable qu'ils font, doivent être respectés. Par conséquent, votre Stream ne devrait jamais survivre en dehors de la durée de vie Dispoable de HttpResponseMessage, car vous n'a pas instancié le Stream.
Dans votre scénario ci-dessus, mon modèle serait de prétendre que l'obtention de ce flux était vraiment juste dans une méthode Static.DoGet (uri), et le flux que vous retournez DEVRAIT être une de nos propres créations. Cela signifie un deuxième Stream, avec le flux de HttpResponseMessage .CopyTo'd mon nouveau Stream (routage via un FileStream ou un MemoryStream ou tout ce qui convient le mieux à votre situation) ... ou quelque chose de similaire. Car:
Alors utilisez des produits jetables comme le catch-and-release ... faites-les, accrochez-vous les résultats par vous-même, libérez-les le plus rapidement possible. Et ne confondez pas l'optimisation et l'exactitude, en particulier dans les classes que vous n'avez pas créées vous-même.