Skip to content

Support binding request body as Stream/ReadOnlySequence<byte> maybe ReadOnlyMemory<byte>/ReadOnlySpan<byte>ย #38153

Closed
@davidfowl

Description

@davidfowl

Is your feature request related to a problem? Please describe.

There's a common pattern where you have a web front end that ingests data and enqueues to into an azure queue (storage or service bus etc) that is then processed by a worker (https://docs.microsoft.com/en-us/azure/architecture/guide/architecture-styles/web-queue-worker). This pattern usually results in a taking an incoming JSON payload, creating a BinaryData from it (this means it's fully buffered) and enqueuing that payload to the queue.

Here's what this might look like in .NET 6:

app.MapPost("v1/feeds", async (QueueClient queueClient, SomeDto dto, CancellationToken cancellationToken) =>
{
    await queueClient.CreateIfNotExistsAsync(cancellationToken: cancellationToken);
    await queueClient.SendMessageAsync(new BinaryData(dto), cancellationToken: cancellationToken);
});

This version has a few costs:

  • We de-serialize the entire JSON payload into the SomeDto object.
  • We serialize the SomeDto object into a single JSON ut8 byte[]
  • If the serialized form is > 85K, it'll end up on the LOH (Large object heap)

Describe the solution you'd like

I would like it to be possible to grab the entire request body as a ReadOnlySequence<byte>, a Stream or a pooled backed Memory<byte>/Span<byte> (though this requires a copy so we should consider this one carefully).

It would enable this:

app.MapPost("v1/feeds", async (QueueClient queueClient, ReadOnlySequence<byte> body, CancellationToken cancellationToken) =>
{
    await queueClient.CreateIfNotExistsAsync(cancellationToken: cancellationToken);
    await queueClient.SendMessageAsync(new BinaryData(body), cancellationToken: cancellationToken);
});

Additional context

  • When injecting the Stream, it would be the same object as HttpRequest.Body
  • We would not buffer the request body by default so after it is read, it wouldn't be rewindable (e.g you can't read the stream multiple times).
  • We would the ReadOnlySequence<byte> would not be usable outside of the handler. Doing so would be dangerous since the underlying memory would be reused outside of this handler. This means fire and forget scenarios should copy the buffer.
  • The ReadOnlyMemory/ReadOnlySpan would be usable outside of the handler since it's a copy of the body.

Metadata

Metadata

Assignees

Labels

DocsThis issue tracks updating documentationPriority:1Work that is critical for the release, but we could probably ship withoutarea-minimalIncludes minimal APIs, endpoint filters, parameter binding, request delegate generator etccost: MWill take from 3 - 5 days to completefeature-minimal-actionsController-like actions for endpoint routingold-area-web-frameworks-do-not-use*DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labelstriage-focusAdd this label to flag the issue for focus at triage

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions