HttpResponseMessage 的处理是调用请求流的处理
Posted
技术标签:
【中文标题】HttpResponseMessage 的处理是调用请求流的处理【英文标题】:Disposing of HttpResponseMessage is calling disposing of request's stream 【发布时间】:2021-10-18 11:32:30 【问题描述】:我有一个方法接受 Stream 参数并将其传递给服务器
public async Task<string> Execute(Stream archive)
archive.Seek(0, SeekOrigin.Begin);
using var content = new MultipartFormDataContent();
content.Add(new StreamContent(archive), "file1", "file1");
var result = "";
using (var response = await _client.PostAsync(_uri, content))
if (response.IsSuccessStatusCode)
var stringResult = await response.Content.ReadAsStringAsync();
result = stringResult;
// here archive is already disposed
return result;
现在我实现了这个方法的重试策略。 如果调用此方法的外部代码得到“”作为结果,则它会尝试再次调用此方法。 但是档案是在那一刻处理的。 我看到归档流在处理响应后立即被处理。 为什么? 如果在这个方法之后需要流参数外面怎么办?
【问题讨论】:
【参考方案1】:StreamContent
将处置Stream
,如it's source 中所述。那将是disposed by the MultipartContent
。这将在 PostAsync 中处理...通过整个链。
解决方案是子类化 Stream 并删除 dispose 方法,如建议的here。但是您必须确保自己处理原始流!
编辑:更新。 Stream
是一个抽象类,所以如果你知道具体的流类型会更容易,例如
public sealed class NoDisposeMemoryStream : MemoryStream
protected override void Dispose(bool disposing)
否则,您将需要编写自己的完整 Stream
包装器,请参见底部。
另一种解决方案是在最里面的 using 块中实现重试机制,每次失败时重置archive
seek origin。这可能更安全。
public sealed class NoDisposeStream : Stream
private Stream _original;
public NoDisposeStream(Stream original) => _original = original
?? throw new ArgumentNullException(nameof(original));
public override bool CanRead => _original.CanRead;
public override bool CanSeek => _original.CanSeek;
public override bool CanWrite => _original.CanWrite;
public override long Length => _original.Length;
public override long Position get => _original.Position; set _original.Position = value;
public override void Flush() => _original.Flush();
public override int Read(byte[] buffer, int offset, int count) => _original.Read(buffer, offset, count);
public override long Seek(long offset, SeekOrigin origin) => _original.Seek(offset, origin);
public override void SetLength(long value) => _original.SetLength(value);
public override void Write(byte[] buffer, int offset, int count) => _original.Write(buffer, offset, count);
protected override void Dispose(bool disposing)
【讨论】:
您需要一个围绕原始流的 wrapper(并且包装器的 dispose 方法为空),而不仅仅是子类Stream
类。
@Evk 使用的StreamContent
构造函数需要Stream
。所以被包裹的东西无论如何都必须来自Stream
,不是吗?我没有完全理解你的意思。
是的,但我的意思是您编写的代码不会编译(Stream 是抽象类),而且我认为通常它没有正确描述这个想法。您需要在NoDisposeStream
的构造函数中传递原始流,然后实现Stream
类的所有方法,委托给该原始流(public override bool CanRead => _original.CanRead
等)。链接答案子类特定MemoryStream
,这里不一样。
@Evk 哦,亲爱的。是的。我实际上并没有尝试使用我链接的答案作为参考。这使它非常难看。但我会做一个。片刻。【参考方案2】:
发生这种情况是因为 HttpClient PostAsync 处理您传递的内容。 https://github.com/microsoft/referencesource/blob/master/System/net/System/Net/Http/HttpClient.cs
相关代码:
public Task<HttpResponseMessage> PostAsync(Uri requestUri, HttpContent content,
CancellationToken cancellationToken)
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUri);
request.Content = content;
return SendAsync(request, cancellationToken);
然后 SendAsync 调用 DisposeRequestContent ,其实现如下:
private void DisposeRequestContent(HttpRequestMessage request)
Contract.Requires(request != null);
// When a request completes, HttpClient disposes the request content so the user doesn't have to. This also
// ensures that a HttpContent object is only sent once using HttpClient (similar to HttpRequestMessages
// that can also be sent only once).
HttpContent content = request.Content;
if (content != null)
content.Dispose();
cmets 说出原因
【讨论】:
以上是关于HttpResponseMessage 的处理是调用请求流的处理的主要内容,如果未能解决你的问题,请参考以下文章
HttpResponseMessage 的处理是调用请求流的处理
在调用ReadAsStreamAsync时何时或何时调用HttpResponseMessage?
异步任务<HttpResponseMessage> 获取 VS HttpResponseMessage 获取
如果 Task<HttpResponseMessage>.IsCompleted 为真,为啥 Task<HttpResponseMessage>.Result 会抛出异常?