如何使用system.io.pipelines.pipereader读取ASP.NET应用中的所有帖子字节?
我正在尝试使用.NET 6从流到pipereader 按Microsoft建议建议。这是我的自定义方法:
private static async Task<byte[]> GetRequestBodyBytesAsync(PipeReader reader)
{
byte[] bodyBytes;
do
{
ReadResult readResult = await reader.ReadAsync();
if (readResult.IsCompleted || readResult.IsCanceled)
{
ReadOnlySequence<byte> slice = readResult.Buffer.Slice(readResult.Buffer.Start, readResult.Buffer.End);
bodyBytes = BuffersExtensions.ToArray(slice);
reader.AdvanceTo(readResult.Buffer.End);
break;
}
} while (true);
return bodyBytes;
}
但是,当我在控制器中使用上述静态方法时:
[HttpPost]
[Route(MyUrl)]
public async Task MyPostAsync()
{
byte[] bodyBytes = await GetRequestBodyBytesAsync(Request.BodyReader);
MyProtobuf myProtobuf = MyProtobuf.Parser.ParseFrom(bodyBytes);
然后,我可以在调试器中看到ReadResult.iscompleted
永远不正确,并且ReadResult.buffer 保持相同的21个字节。
添加else {Reader.advanceto(ReadResult.Buffer.start); }
尚未更改任何东西。
我需要完整的字节数组,以便可以将其传递给Protobuf解析方法(Protobuf没有流解析器)...我该如何在这里使用io.pipelines?
更新:
以下方法(在 pipereadeRextensions.cs 并由Marc确认)现在为我工作,甚至可以通过避免复制数据来进一步改进 - 请参阅下面的MARC的出色评论。
private static async Task<byte[]> GetRequestBodyBytesAsync(PipeReader reader)
{
do
{
ReadResult readResult = await reader.ReadAsync();
if (readResult.IsCompleted || readResult.IsCanceled)
{
return readResult.Buffer.ToArray();
}
// consume nothing, keep reading from the pipe reader until all data is there
reader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.End);
} while (true);
}
I am trying to switch my ASP.NET application using .Net 6 from Stream to PipeReader as recommended by Microsoft. Here is my custom method:
private static async Task<byte[]> GetRequestBodyBytesAsync(PipeReader reader)
{
byte[] bodyBytes;
do
{
ReadResult readResult = await reader.ReadAsync();
if (readResult.IsCompleted || readResult.IsCanceled)
{
ReadOnlySequence<byte> slice = readResult.Buffer.Slice(readResult.Buffer.Start, readResult.Buffer.End);
bodyBytes = BuffersExtensions.ToArray(slice);
reader.AdvanceTo(readResult.Buffer.End);
break;
}
} while (true);
return bodyBytes;
}
However when I use the above static method in my controller:
[HttpPost]
[Route(MyUrl)]
public async Task MyPostAsync()
{
byte[] bodyBytes = await GetRequestBodyBytesAsync(Request.BodyReader);
MyProtobuf myProtobuf = MyProtobuf.Parser.ParseFrom(bodyBytes);
then I can see in the debugger that the readResult.IsCompleted
is never true and the readResult.Buffer
stays the same 21 bytes.
Adding else { reader.AdvanceTo(readResult.Buffer.Start); }
has not changed anything.
I need the complete byte array, so that I can pass it to Protobuf parsing method (there are no streaming parsers for protobuf)... how can I please use IO.Pipelines here?
UPDATE:
The following method (found in PipeReaderExtensions.cs and confirmed by Marc) works now for me and it even can be improved further by avoiding copying data around - see the great comments by Marc below.
private static async Task<byte[]> GetRequestBodyBytesAsync(PipeReader reader)
{
do
{
ReadResult readResult = await reader.ReadAsync();
if (readResult.IsCompleted || readResult.IsCanceled)
{
return readResult.Buffer.ToArray();
}
// consume nothing, keep reading from the pipe reader until all data is there
reader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.End);
} while (true);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您本来打算在每个
readasync
之后调用advanceto
,因此您应该真正拥有一个else
来做类似的事情:这说明“我已经检查了一切,什么也没消耗”(至少在语义上;您还没有真正看过的任何东西都没关系 - 关键是说“所有这些字节:对我没有用”) - 这意味着
readasync
现在不应该尝试给您任何其他东西,直到它拥有一些其他数据或数据已结束。实际上,我还没有抛出例外情况,我感到有些惊讶。You are meant to call
AdvanceTo
after everyReadAsync
, so you should really have anelse
that does something like:This says "I've checked everything, and consumed nothing" (at least semantically; it doesn't matter that you haven't really looked at anything - the point is to say "all of these bytes: aren't useful to me yet") - which means that
ReadAsync
now shouldn't try to give you anything else until it has some additional data or the data has ended. I'm a little surprised that it didn't throw an exception already, actually.