dotnet/aspnetcore

Add Results.Stream overload that takes a Func<Stream, Task>

Open

#39,383 opened on Jan 8, 2022

View on GitHub
 (20 comments) (4 reactions) (1 assignee)C# (10,653 forks)batch import
api-approvedarea-minimalfeature-minimal-actionshelp wanted

Repository metrics

Stars
 (37,933 stars)
PR merge metrics
 (Avg merge 16d 9h) (258 merged PRs in 30d)

Description

Background and Motivation

There are scenarios where APIs want to work directly on the response stream without buffering. Today the Stream overloads assume you have a stream that you have produced that is then copied to the underlying response stream, instead, we want to provide a result type that gives you access to the underlying response stream.

Example:

https://github.com/khalidabuhakmeh/dotnet-dramameter/blob/main/DramaMeter/Program.cs#L37-L43

Another is writing a blob storage results to the http response.

Proposed API

namespace Microsoft.AspNetCore.Http
{
    public static class Results
    {
+       public IResult Stream(Func<Stream, Task> streamWriterCallback, 
+                             string? contentType = null,
+                             string? fileDownloadName = null,
+                             DateTimeOffset? lastModified = null,
+                             EntityTagHeaderValue? entityTag = null,
+                             bool enableRangeProcessing = false);
    }
}

Usage Examples

app.MapGet("/", async (HttpContext http, string? level) => {
    level ??= "low";

    if (!levels.TryGetValue(level.Trim(), out var result))
        return Results.BadRequest("invalid level");

    var image = background.CloneAs<Rgba32>();
    image.Mutate(ctx => {
        ctx.Vignette(result.color); // match the background to the intensity
        ctx.DrawImage(foreground, new Point(0, 0), 1f);
        ctx.DrawImage(result.image, new Point(0, 0), opacity: 1f);
    });
    
    http.Response.Headers.CacheControl = $"public,max-age={FromHours(24).TotalSeconds}";
    return Results.Stream(stream => image.SaveAsync(stream, PngFormat.Instance), "image/png");
});

Risks

None

Contributor guide