2014-12-31 20 views
22

Tôi đang sử dụng System.Net.Http.HttpClient để thực hiện một số giao tiếp HTTP phía máy khách. Tôi đã có tất cả các HTTP tại một chỗ, trừu tượng ra khỏi phần còn lại của mã. Trong một trường hợp, tôi muốn đọc nội dung phản hồi dưới dạng luồng, nhưng người tiêu dùng của luồng được cách ly tốt từ nơi giao tiếp HTTP xảy ra và luồng được mở. Tại chỗ chịu trách nhiệm về giao tiếp HTTP, tôi đang xử lý tất cả các công cụ HttpClient.Khi nào hoặc nếu vứt bỏ HttpResponseMessage khi gọi ReadAsStreamAsync?

đơn vị này kiểm tra sẽ thất bại tại 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 
} 

tôi thường cố gắng vứt bỏ bất cứ điều gì IDisposable tại thuận tiện sớm nhất có thể nhưng trong trường hợp này, xử lý các HttpResponseMessage cũng disposes các Stream trở về từ ReadAsStreamAsync. Vì vậy, có vẻ như mã gọi cần biết và nắm quyền sở hữu thông điệp phản hồi cũng như luồng hoặc tôi để lại thông báo phản hồi không mong muốn và để cho đối tác cuối cùng xử lý nó. Cả hai tùy chọn đều không đúng.

This answer nói về việc không xử lý HttpClient. Làm thế nào về HttpRequestMessage và/hoặc HttpResponseMessage?

Tôi có thiếu gì đó không? Tôi hy vọng sẽ giữ cho mã tiêu thụ không biết gì về HTTP nhưng để lại tất cả những đối tượng không rõ ràng xung quanh đi ngược lại năm thói quen!

+0

Chỉ cần một mẹo - Không phải mọi thứ 'IDisposable' cần phải được xử lý –

+0

này dường như không có bất cứ điều gì để làm với 'async' cho mỗi gia nhập. Các quy tắc cũng giống nhau: không bỏ đối tượng cho đến khi bạn hoàn thành nó. Điều tương tự cũng sẽ áp dụng khi sử dụng phiên bản đồng bộ. Vì vậy, sử dụng 'Stream' _inside_ trả về' using'. Nếu bạn cần sử dụng 'Stream' bên ngoài ngữ cảnh nơi yêu cầu được tạo, bạn sẽ phải thiết lập một cơ chế khác để xử lý nó vào đúng thời điểm. –

+0

Ngoài ra: Tôi không khuyên bạn nên bỏ việc xử lý cho finalizer ...nhưng tôi lưu ý rằng bạn không bận tâm đến việc vứt bỏ 'client', vì vậy nếu bạn cảm thấy thoải mái với điều đó, đừng bận tâm với những thứ khác. Đối với câu trả lời bạn tham khảo, lưu ý rằng nó liên quan đến kịch bản mà bạn _reuse_ đối tượng 'HttpClient'; thẳng thắn, rõ ràng là nếu bạn muốn sử dụng lại nó, bạn không vứt bỏ nó. Các hướng dẫn không nói bất cứ điều gì ở tất cả về việc liệu nó là hợp pháp để chỉ cho phép xử lý đối tượng xảy ra thông qua hoàn thiện (và IMHO nó là hình thức rất nghèo). –

Trả lời

8

Vì vậy, nó có vẻ như đoạn code gọi cần biết về và mất quyền sở hữu của thông điệp phản ứng cũng như dòng suối, hoặc tôi rời thông điệp phản ứng chưa ga và để cho các thỏa thuận finalizer với nó. Không có tùy chọn nào phù hợp.

Trong trường hợp cụ thể này, không có finalizers. Không HttpResponseMessage hoặc HttpRequestMessage triển khai trình hoàn thiện (và đó là điều tốt!). Nếu bạn không thải bỏ một trong số chúng, chúng sẽ bị thu gom rác khi GC bắt đầu và xử lý các luồng cơ bản của chúng sẽ được thu thập khi điều đó xảy ra.

Miễn là bạn đang sử dụng các đối tượng này, đừng thải bỏ. Sau khi hoàn tất, xử lý chúng. Thay vì gói chúng trong tuyên bố using, bạn luôn có thể gọi một cách rõ ràng Dispose khi bạn đã hoàn tất. Dù bằng cách nào thì mã tiêu thụ không cần phải có bất kỳ kiến ​​thức cơ bản nào về các yêu cầu http.

+0

Đó không phải là một điều tốt cả! Để các đối tượng HttpMessageRequest & HttpMessageResponse chưa được xử lý có nghĩa là chúng sẽ chỉ được xử lý bởi GC (bất cứ khi nào nó có thể quyết định chạy) và cho đến lúc đó tất cả các tài nguyên được sử dụng cho cuộc gọi sẽ được giải phóng. Điều này dẫn đến tình trạng cạn kiệt cổng, hết thời gian chờ nếu có giới hạn đối với các yêu cầu HTTP đồng thời, v.v ... –

+0

@AlonCatz Câu trả lời của tôi ở đâu tôi nói không được vứt bỏ đồ vật? Bạn đã đọc đoạn thứ hai chưa? –

+0

@AlonCatz - GC ** không bao giờ ** gọi '.Dispose()' theo đúng nghĩa của nó. Chỉ khi có một trình hoàn chỉnh gọi là '.Dispose()' mà GC có thể kích hoạt việc vứt bỏ. – Enigmativity

1

Bạn cũng có thể lấy luồng làm thông số đầu vào, vì vậy người gọi có toàn quyền kiểm soát loại luồng cũng như xử lý nó. Và bây giờ bạn cũng có thể vứt bỏ httpResponse trước khi kiểm soát rời khỏi phương thức.
Dưới đây là phương pháp mở rộng cho 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);     
     } 
    } 
Các vấn đề liên quan