2014-12-31 13 views
22

Używam System.Net.Http.HttpClient do wykonywania komunikacji HTTP po stronie klienta. Mam wszystkie HTTP w jednym miejscu, oddzielone od reszty kodu. W jednym przypadku chcę odczytać zawartość odpowiedzi jako strumień, ale konsument strumienia jest dobrze odizolowany od miejsca, w którym odbywa się komunikacja HTTP i strumień jest otwarty. W miejscu odpowiedzialnym za komunikację HTTP pozbywam się wszystkich rzeczy z HttpClient.Kiedy lub kiedy wyrzucić HttpResponseMessage podczas wywoływania ReadAsStreamAsync?

Test

Jednostka ta nie powiedzie się 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 
} 

ja zazwyczaj staram się wrzucać wszystkiego IDisposable na możliwie najwcześniejszym wygody, ale w tym przypadku umieszczenia HttpResponseMessage dysponuje również Stream wrócił z ReadAsStreamAsync.

Wygląda na to, że kod wywoławczy musi wiedzieć i przejąć na własność wiadomość z odpowiedzią, a także strumień, lub pozostawić wiadomość z odpowiedzią i nie pozwolić, by finalizator sobie z nią poradził. Żadna z tych opcji nie wydaje się właściwa.

This answer mówi o nie wyrzucaniu HttpClient. Co powiesz na HttpRequestMessage i/lub HttpResponseMessage?

Czy brakuje mi czegoś? Mam nadzieję, że zachowam niedostrzegalny kod HTTP, ale pozostawienie tych wszystkich nieporządanych obiektów wbrew zwyczajowi!

+0

Tylko końcówka - Nie wszystko 'IDisposable' należy wyrzucać –

+0

ten wydaje się nie mieć nic wspólnego z' async' per se. Zasady są takie same w każdym przypadku: nie wyrzucaj obiektu, dopóki go nie skończysz. To samo dotyczyłoby wersji synchronicznej. Tak więc użyj zwróconego 'Stream' _inside_ the" using ". Jeśli chcesz użyć 'Stream' poza kontekstem, w którym tworzone jest żądanie, musisz skonfigurować inny mechanizm, aby usunąć go we właściwym czasie. –

+0

Ponadto: nie polecam pozostawiania utylizacji dla finalizatora ...ale zauważam, że i tak nie trudzisz się wyrzucaniem 'klienta', więc jeśli nie masz nic przeciwko temu, nie przejmuj się innymi rzeczami. Jeśli chodzi o odpowiedź, na którą się powołujesz, zauważ, że dotyczy ona scenariusza, w którym "korzystasz" z obiektu 'HttpClient'; Szczerze mówiąc, powinno być oczywiste, że jeśli chcesz go ponownie użyć, nie pozbądź się go. Wskazówki nie mówią nic o tym, czy dozwolone jest po prostu oddanie obiektu przez sfinalizowanie (a IMHO jest bardzo słaba). –

Odpowiedz

8

Więc wydaje się, że kod wywołujący musi wiedzieć i wziąć własność komunikacie odpowiedzi, jak również strumień, czy mogę zostawić komunikat odpowiedzi niezdyspergowanego i niech ofertę finalizatora z nim. Żadna z opcji nie wydaje się właściwa.

W tym konkretnym przypadku nie ma finalizatorów. Ani HttpResponseMessage ani HttpRequestMessage nie implementują finalizatora (i to dobrze!). Jeśli nie pozbędziesz się któregokolwiek z nich, zostaną zebrane śmieci, gdy GC się pojawi, a uchwyt do ich bazowych strumieni zostanie zebrany, gdy to nastąpi.

Dopóki używasz tych obiektów, nie wyrzucaj ich. Po zakończeniu, pozbyć się ich. Zamiast owijać je w oświadczeniu using, zawsze możesz jednoznacznie wywołać Dispose, gdy skończysz. Tak czy inaczej, zużywający się kod nie musi mieć żadnej wiedzy na temat żądań HTTP.

+0

To wcale nie jest dobra rzecz! Pozostawienie nieudzielonych obiektów HttpMessageRequest & HttpMessageResponse oznacza, że ​​będą one usuwane tylko przez GC (gdy tylko zdecyduje się uruchomić) i do tego czasu wszystkie zasoby użyte do wywołania zostaną zwolnione. Prowadzi to do wyczerpania portu, przekroczenia limitu czasu na współbieżne żądania HTTP, itp. ... –

+0

@AlonCatz Gdzie w mojej odpowiedzi powiedziałem, żeby nie wyrzucać obiektów? Czy przeczytałeś drugi akapit? –

+0

@AlonCatz - GC ** never ** wywołuje '.Dispose()' sam w sobie. Jest tylko wtedy, gdy jest finalizator, który wywołuje '.Dispose()', że GC może wywołać dispose. – Enigmativity

1

Możesz również przyjąć strumień jako parametr wejściowy, dzięki czemu dzwoniący ma pełną kontrolę nad typem strumienia, jak również jego usuwanie. A teraz możesz także wyrzucić httpResponse, zanim kontrola opuści metodę.
Poniżej jest metoda rozszerzenie dla 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);     
     } 
    } 
Powiązane problemy