HttpResponseMessage 的处理是调用请求流的处理
Disposing of HttpResponseMessage is calling disposing of request's stream
我有一个获取 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;
}
现在我实现这个方法的重试策略。
如果调用此方法的外部代码得到 "" 作为结果,则它会尝试再次调用此方法。
但档案是为那一刻准备的。
我看到存档流在处理响应后立即处理。
为什么?
这个方法后需要外面的stream参数怎么办?
StreamContent
将处理 Stream
,如 it's source. And that will be disposed by the MultipartContent
中所述。这将在 PostAsync 中处理...整个链。
一个解决方案是 subclass Stream 并删除 dispose 方法,就像提议的 here 一样。但您必须确保自己处理原始流!
编辑:更新。 Stream
是一个抽象class,所以如果你知道具体的流类型会更容易,例如
public sealed class NoDisposeMemoryStream : MemoryStream
{
protected override void Dispose(bool disposing) { }
}
否则您将需要编写自己的完整 Stream
包装器,见底部。
另一种解决方案是在最里面的 using 块中实现重试机制,在每次失败时重置 archive
寻源。这可能更安全。
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) { }
}
发生这种情况是因为 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();
}
}
评论说原因
我有一个获取 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;
}
现在我实现这个方法的重试策略。 如果调用此方法的外部代码得到 "" 作为结果,则它会尝试再次调用此方法。 但档案是为那一刻准备的。 我看到存档流在处理响应后立即处理。 为什么? 这个方法后需要外面的stream参数怎么办?
StreamContent
将处理 Stream
,如 it's source. And that will be disposed by the MultipartContent
中所述。这将在 PostAsync 中处理...整个链。
一个解决方案是 subclass Stream 并删除 dispose 方法,就像提议的 here 一样。但您必须确保自己处理原始流!
编辑:更新。 Stream
是一个抽象class,所以如果你知道具体的流类型会更容易,例如
public sealed class NoDisposeMemoryStream : MemoryStream
{
protected override void Dispose(bool disposing) { }
}
否则您将需要编写自己的完整 Stream
包装器,见底部。
另一种解决方案是在最里面的 using 块中实现重试机制,在每次失败时重置 archive
寻源。这可能更安全。
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) { }
}
发生这种情况是因为 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();
}
}
评论说原因