Stream HTTP Response Content in ASP.NET Core Web API
How to stream an the HTTP response in .NET Core WebAPI
UPDATE: Article continued with part 2, advanced HTTP Streaming:
Stream HTTP Response Content in ASP.NET Core Web API - Part 2, infinite βΎοΈ data stream
So I came upon this issue these days.
Couldn't find a spot-on response anywhere on the web. It's either not well documented or not very well indexed because neither StackOverflow or MSDN or Google where of any help.
Any post I found was either suggesting PushStreamContent
, which was available up until .NET Core 2.2, or IAsyncEnumerable
which is not an option if you're using .NET Standard 2.0.
So what to do? The solution was surprisingly easy!
Return a Stream
:
[Route(nameof(StreamSomethig)), HttpGet]
public Task<Stream> StreamSomethig()
{
Response.ContentType = "application/json; charset=utf-8";
return useCase.GenerateStream();
}
[...]
using H.Necessaire;
public async Task<Stream> GenerateStream()
{
using(IDisposableEnumerable<MyData> dataStream = await manager.StreamData())
{
return await dataStream.ToJsonUTF8Stream();
}
}
[...]
using H.Necessaire;
using H.Necessaire.Serialization;
public static async Task<Stream> ToJsonUTF8Stream<T>(this IEnumerable<T> collection)
{
MemoryStream result = new MemoryStream();
if (collection?.Any() != true)
{
await "[]".WriteToStreamAsync(result);//extension from H.Necessaire
result.Position = 0;
return result;
}
await "[".WriteToStreamAsync(result);//extension from H.Necessaire
foreach (T entry in collection)
{
await entry.ToJsonObject().WriteToStreamAsync(result);//extension from H.Necessaire and H.Necessaire.Serialization
await ",".WriteToStreamAsync(result);//extension from H.Necessaire
}
result.Position -= 1;
await "]".WriteToStreamAsync(result);//extension from H.Necessaire
result.Position = 0;
return result;
}
H.Necessaire NuGets:
Core: https://www.nuget.org/packages/H.Necessaire
Serialization: https://www.nuget.org/packages/H.Necessaire.Serialization
The code above will stream the response (aka send data in chunks) Β to the client instead of doing a full-load/full-dump, thus saving server-side memory and allowing the client to react while the response is downloading. Similar to streaming a video on YouTube.
Needless to say this technique is to be used when the returned payload is huge, because the partial response is obviously not a valid JSON until it's fully loaded!
Which means that the consuming client needs to be aware of this and implement specific techniques to parse the response as it loads. Extra effort!
I'll post a .NET consuming client sample at some point.