c# - StringContent 是否与 HttpResponseMessage 一起处理?
问题描述
StringContent/HttpContent
是一次性的,HttpRequestMessage
我想知道请求是否在StringContent
被处理时HttpRequestMessage
被处理,或者我是否需要两个单独using
的,或者是否有更好的方法来处理这些?例如
var content = new StringContent("test");
using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUri))
{
requestMessage.Content = content;
var response = await HttpClient.SendAsync(requestMessage).ConfigureAwait(false);
}
这是否需要:
using(var content = new StringContent("test"))
{
using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUri))
{
requestMessage.Content = content;
var response = await HttpClient.SendAsync(requestMessage).ConfigureAwait(false);
}
}
解决方案
(在撰写本文时,OP 的问题对于它们是否指的是HttpRequestMessage
or是模棱两可的HttpResponseMessage
,所以我将同时描述两者)。
HttpRequestMessage.Dispose()
:- 仅处理请求的
Content
. - 请参阅此处的源代码。
- 仅处理请求的
HttpResponseMessage.Dispose()
:- 仅处理其
response.Content
. - 它不会以任何其他方式
处置
response.RequestMessage
或处置。HttpRequestMessage
- 因此,它不会处理
HttpRequestMessage.Content
任何一个。
- 因此,它不会处理
- 请参阅此处的源代码。
- 仅处理其
在完全完成HttpRequestMessage
发送您的请求之前,您不应该处理它(我相信在某些(罕见的)情况下,即使请求的内容尚未完成发送,您也会得到回复 - 比如使用外来传输编码方案时或多部分请求) - 但通常最好假设所有涉及的对象的嵌套生命周期,所以 _if是完美的(它不是)那么你会这样做:HttpClient
HttpResponseMessage
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
andHttpResponseMessage
对象是分开的,所以你可以处理你的HttpRequestMessage
但仍然返回HttpResponseMessage
(反之亦然!)(尽管你不能返回Stream
fromResponseMessage.Content.ReadAsStream()
如果你处理了HttpResponseMessage
- 所以如果你需要返回 aStream
你应该继承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.
}
推荐阅读
- github - Repository-Webhooks vs Github Apps - 使用哪个?
- mongodb - 服务 mongod 没有在 Centos8 上启动
- java - ShakeDetector 在 Fragment 中不起作用,我该怎么办
- raspberry-pi - Grove I2C 显示器无法在 Raspberry Pi 3B 上运行
- python - 使用 Spacy 的句子情感评分
- oracle - 如何从 Azure 数据工厂 v2 调用 Oracle 存储过程
- python - 第一次训练数据,我发现线性回归模型中的“训练”有问题
- java - Maven部署未能部署工件:找不到工件
- php - 使用简码显示选定的 WooCommerce 变体格式的价格
- amazon-web-services - Terraform:捕获 lambda 删除事件