我正在使用System.Net.Http.HttpClient
进行一些客户端HTTP通信。我将所有HTTP放在一个位置,与其余代码分离。在一个示例中,我希望以流的形式读取响应内容,但是流的消费者与HTTP通信发生和流被打开的地方很好地隔离。在负责HTTP通信的地方,我处理了所有的HttpClient
东西。
此单元测试将在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
}
我通常会尽可能早地处理掉IDisposable
中的任何内容,但在本例中,处理HttpResponseMessage
也会处理掉从ReadAsStreamAsync
返回的Stream
。
因此,似乎调用代码需要知道响应消息和流并获得它们的所有权,或者我不处理响应消息,让终结器来处理它。
This answer谈到不处理HttpClient
,那么HttpRequestMessage
和/或HttpResponseMessage
呢?
我错过了什么吗?我希望让消费代码不知道HTTP,但留下所有这些未处理的对象违背了多年的习惯!
5条答案
按热度按时间wpx232ag1#
因此,似乎调用代码需要知道响应消息和流并获得它们的所有权,或者我不处理响应消息,让终结器来处理它。
在这个特定的例子中,* 没有终结器 *。
HttpResponseMessage
和HttpRequestMessage
都没有实现终结器(这是一件好事!)。如果你不处理它们中的任何一个,一旦GC启动,它们就会被垃圾收集,一旦发生这种情况,它们底层流的句柄就会被收集。只要你在使用这些对象,就不要dispose。一旦完成,就 dispose of them。不用把它们 Package 在
using
语句中,你总是可以在完成后显式调用Dispose
。无论哪种方式,使用代码都不需要了解http请求的底层知识。f4t66c6m2#
你也可以把stream作为输入参数,这样调用者就可以完全控制流的类型和处理,现在你也可以在控制权离开方法之前处理httpResponse。
下面是HttpClient的扩展方法
eaf3rand3#
在.NET中处理一次性的东西既容易又困难。
流也是一样的废话......释放缓冲区也会自动释放它 Package 的流吗?应该吗?作为使用者,我甚至应该知道它是否会这样做吗?
当我处理这些事情的时候,我有一些规则:
1.如果我认为有非本机资源在起作用(比如网络连接),我绝不会让GC“去处理它”。资源耗尽是真实的的,好的代码会处理它。
1.如果Disposable将Disposable作为参数,那么保护代码并确保代码处理它生成的每一个对象是没有坏处的。如果代码没有处理,我可以忽略它。
因此,您有一个HttpClient、一个HttpRequestMessage和一个HttpResponseMessage。它们中的每一个以及它们所创建的任何Disposable的生存期都必须得到尊重。因此,您的
Stream
不应该在HttpResponseMessage
的Disposable生存期之外继续存在,因为 * 您 * 没有示例化Stream。在上面的场景中,我的模式是假设获取
Stream
实际上只是在Static.DoGet中(uri)方法,并且您返回的Stream必须是我们自己创建的。这意味着第二个Stream,CopyTo我的新流(通过FileStream
或MemoryStream
或任何最适合您的情况的路由)...或类似的东西,因为:HttpResponseMessage's
Stream的生命。那是他的,不是你的。:)HttpClient
这样的可释放对象的生存期是一个疯狂的阻塞器,这就像在解析DataTable时占用SqlConnection
一样(想象一下,如果DataTable变得很大,我们会多么快地耗尽连接池)HttpResponseMessage
,它是一次性的,但这只是因为我们使用了HttpClient
和HttpRequestMessage
,它们是一次性的......而您想要的只是一个来自URI的流。因此,使用像捕捉和释放这样的一次性工具......制作它们,自己捕捉结果,尽快释放它们。不要把优化与正确性混为一谈,尤其是对于那些不是你自己编写的类。
vnjpjtjt4#
不要释放
HttpResponseMessage
,因为这是调用此方法的一方的职责。方法:
用法:
uyhoqukh5#
经过几个小时的思考,我得出了这样一个结论:
一个适配器,它将
HttpRequestMessage
及其内容流作为依赖项。这就是它。请密切注意它的静态工厂方法
Create
。由于显而易见的原因,构造函数是私有的。检查此示例用法:
重要的是,处理这个 Package 器也会处理响应,从而能够有效地控制生命周期。
这样,你就可以安全地创建像这个方法一样的泛型消费者,它不关心底层实现的任何事情:
像这样使用它:
DoSomething
方法完全忽略了与处理相关的不满,它只是像处理其他流一样处理流,并且处理在内部进行。希望这能帮上忙。