首页 > 解决方案 > 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);
    }
}

标签: c#asp.net.netasp.net-core.net-core

解决方案


(在撰写本文时,OP 的问题对于它们是否指的是HttpRequestMessageor是模棱两可的HttpResponseMessage,所以我将同时描述两者)。

  • HttpRequestMessage.Dispose()
  • HttpResponseMessage.Dispose()
    • 处理response.Content.
    • 不会以任何其他方式 处置response.RequestMessage或处置。HttpRequestMessage
      • 因此,它不会处理HttpRequestMessage.Content任何一个。
    • 请参阅此处的源代码

完全完成HttpRequestMessage发送您的请求之前,您不应该处理它(我相信在某些(罕见的)情况下,即使请求的内容尚未完成发送,您也会得到回复 - 比如使用外来传输编码方案时或多部分请求) - 但通常最好假设所有涉及的对象的嵌套生命周期,所以 _if是完美的(它不是)那么你会这样做:HttpClientHttpResponseMessageSystem.Net.Http

  1. 确保请求/响应完全完成。
  2. 首先处理响应内容。
  3. 然后HttpResponseMessage.
  4. 然后是请求的内容。
  5. 然后HttpRequestMessage.
  6. 然后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;
}

虽然记住HttpRequestMessageandHttpResponseMessage对象是分开的,所以你可以处理你的HttpRequestMessage但仍然返回HttpResponseMessage(反之亦然!)(尽管你不能返回StreamfromResponseMessage.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.
}

推荐阅读