(在撰写本文时,OP 的问题是模棱两可,他们指的是HttpRequestMessage 还是HttpResponseMessage,所以我将同时描述两者。
-
HttpRequestMessage.Dispose():
-
HttpResponseMessage.Dispose():
-
仅处置其
response.Content。
- 它不会以任何其他方式处置
response.RequestMessage 或HttpRequestMessage。
- 因此,它也不会处理
HttpRequestMessage.Content。
-
See the source code here。
在HttpClient 完全发送完您的请求之前,您不应该处置HttpRequestMessage(我相信在某些(罕见的)情况下,您即使请求的内容还没有完成发送,也得到一个HttpResponseMessage 回复——比如在使用奇异的传输编码方案或多部分请求时)——但通常最好假设所有的嵌套生命周期涉及的对象,所以 _if System.Net.Http 是完美的(它不是)那么你会这样做:
- 确保请求/响应完全完成。
- 首先处理响应内容。
- 然后是
HttpResponseMessage。
- 然后是请求的内容。
- 然后是
HttpRequestMessage。
- 然后是
HttpClient。
...但是,当您处理父请求/响应消息时,处理内容是隐式的(并且您可能也不应该调用HttpClient.Dispose),所以只需这样做:
using (HttpClient httpClient = this.httpClientFactory.CreateHttpClient()) // Only dispose HttpClient instances created by IHttpClientFactory. *DO NOT DISPOSE* of other HttpClient instances unless you know what you're doing!
using (HttpContent reqContent = new StringContent("test")) // Disposing of this HttpContent is unnecessary (as it's disposed when `request` is disposed).
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUri) { Content = reqContent })
using (HttpResponseMessage response = await httpClient.SendAsync(request).ConfigureAwait(false)) // Note there is no `using()` block for the HttpContent object, this is because we don't "own" the object, the HttpResponseMessage does.
{
String responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
return responseBody;
}
尽管请记住 HttpRequestMessage 和 HttpResponseMessage 对象是分开的,因此您可以处理您的 HttpRequestMessage 但仍然返回 HttpResponseMessage(反之亦然!)(尽管您不能 em> 如果您确实处理了HttpResponseMessage,则从ResponseMessage.Content.ReadAsStream() 返回Stream - 所以如果您需要返回Stream,您应该继承Stream 并包装返回的stream,但不要处理HttpResponseMessage 而是将其丢弃在您的 Stream 子类的 Dispose 方法中。
public async Task<Stream> GetStreamAsync()
{
using( HttpClient httpClient = this.httpClientFactory.CreateHttpClient() )
using( HttpContent reqContent = new StringContent("test") )
using( HttpRequestMessage request = new HttpRequestMessage( HttpMethod.Post, requestUri) { Content = reqContent })
{
HttpResponseMessage response = await this.httpClient.SendAsync( request ).ConfigureAwait(false);
try
{
Stream stream = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
return new StreamWithDependencies( stream, response ); // See `class StreamWithDependencies` below.
}
catch // <-- When returning an IDisposable you should only dispose of things in `catch`, not in a `finally`.
{
response.Dispose(); // <-- Doing this means `response` (and `response.Content` will always be disposed) *and* can optionally outlive the method's scope if it follows the happy-path.
throw; // <-- Don't swallow any exceptions, re-throw them (don't use `throw ex;` as that resets the StackTrace, always use either `throw;` (without a name) or throw a new exception with the caught exception passed as the `Exception.InnerException`.)
}
}
}
这是class Stream2 的示例:主要是它代理Stream,并且它的Dispose 方法将处理您告诉它的其他对象。
using System.Collections.Immutable;
using System.IO;
class StreamWithDependencies : Stream
{
private readonly Stream subject;
private readonly ImmutableList<IDisposable> disposeWith;
public StreamWithDependencies( Stream subject, params IDisposable[] disposeWith )
{
this.subject = subject ?? throw new ArgumentNullException(nameof(subject));
this.disposeWith = ( disposeWith ?? Array.Empty<IDisposable>() ).ToImmutableList();
}
protected override void Dispose(Boolean disposing)
{
if( disposing )
{
this.subject.Dispose();
foreach( IDisposable d in this.disposeWith )
{
d.Dispose();
}
}
base.Dispose(disposing);
}
public override Int32 Read(...) => this.subject.Read( ... ):
pubiic override Task<Int32> ReadAsync( ... ) => this.Subject.ReadAsync( ... );
// etc
// Only implement and forward methods for reading, no need to implement methods for writing.
}