如何使用system.io.pipelines.pipereader读取ASP.NET应用中的所有帖子字节?

发布于 2025-02-05 23:48:31 字数 2097 浏览 2 评论 0原文

我正在尝试使用.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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

∞琼窗梦回ˉ 2025-02-12 23:48:31

您本来打算在每个readasync之后调用advanceto,因此您应该真正拥有一个else来做类似的事情:

reader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.End);

这说明“我已经检查了一切,什么也没消耗”(至少在语义上;您还没有真正看过的任何东西都没关系 - 关键是说“所有这些字节:对我没有用”) - 这意味着readasync 现在不应该尝试给您任何其他东西,直到它拥有一些其他数据数据已结束。实际上,我还没有抛出例外情况,我感到有些惊讶。

You are meant to call AdvanceTo after every ReadAsync, so you should really have an else that does something like:

reader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.End);

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文