diff --git a/aspnetcore/fundamentals/minimal-apis/bindStreamPipeReader/7.0-samples/PipeStreamToBackgroundQueue/BackgroundQueueService.cs b/aspnetcore/fundamentals/minimal-apis/bindStreamPipeReader/7.0-samples/PipeStreamToBackgroundQueue/BackgroundQueueService.cs new file mode 100644 index 000000000000..4a271c5cbaec --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/bindStreamPipeReader/7.0-samples/PipeStreamToBackgroundQueue/BackgroundQueueService.cs @@ -0,0 +1,40 @@ +using System.Text.Json; +using System.Threading.Channels; + +namespace BackgroundQueueService; + +class BackgroundQueue : BackgroundService +{ + private readonly Channel> _queue; + private readonly ILogger _logger; + + public BackgroundQueue(Channel> queue, ILogger logger) + { + _queue = queue; + _logger = logger; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + await foreach (var dataStream in _queue.Reader.ReadAllAsync(stoppingToken)) + { + try + { + var person = JsonSerializer.Deserialize(dataStream.Span)!; + _logger.LogInformation($"{person.Name} is {person.Age} years and from {person.Country}"); + // you could do something else with the data + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } + } + } +} + +class Person +{ + public string Name { get; set; } = String.Empty; + public int Age { get; set; } + public string Country { get; set; } = String.Empty; +} diff --git a/aspnetcore/fundamentals/minimal-apis/bindStreamPipeReader/7.0-samples/PipeStreamToBackgroundQueue/PipeStreamToBackgroundQueue.csproj b/aspnetcore/fundamentals/minimal-apis/bindStreamPipeReader/7.0-samples/PipeStreamToBackgroundQueue/PipeStreamToBackgroundQueue.csproj new file mode 100644 index 000000000000..4c2bb77d0106 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/bindStreamPipeReader/7.0-samples/PipeStreamToBackgroundQueue/PipeStreamToBackgroundQueue.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/aspnetcore/fundamentals/minimal-apis/bindStreamPipeReader/7.0-samples/PipeStreamToBackgroundQueue/Program.cs b/aspnetcore/fundamentals/minimal-apis/bindStreamPipeReader/7.0-samples/PipeStreamToBackgroundQueue/Program.cs new file mode 100644 index 000000000000..86f8f8d9e2c6 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/bindStreamPipeReader/7.0-samples/PipeStreamToBackgroundQueue/Program.cs @@ -0,0 +1,59 @@ +using System.Threading.Channels; +using BackgroundQueueService; + +var builder = WebApplication.CreateBuilder(args); +// The max memory we're willing to use for the upload endpoint on this instance +var maxMemory = 500 * 1024 * 1024; + +// The max size of a single message, we're staying below the default LOH size of 85K +var maxMessageSize = 80 * 1024; + +// The max size of the queue based on those restrictions +var maxQueueSize = maxMemory / maxMessageSize; + +// Create a channel to send data to the background queue. +builder.Services.AddSingleton>>((_) => Channel.CreateBounded>(maxQueueSize)); + +// Create a background queue service. +builder.Services.AddHostedService(); +var app = builder.Build(); + +// curl --request POST 'http://localhost:5256/register' --header 'Content-Type: application/json' --data-raw '{ "Name":"Samson", "Age": 23, "Country":"Nigeria" }' +app.MapPost("/register", async (HttpRequest req, Stream body, Channel> queue) => +{ + if (req.ContentLength is not null && req.ContentLength > maxMessageSize) + { + // Message size exceeded + return Results.BadRequest(); + } + + // We're not above the message size and we have a content length, or + // we're a chunked request and we're going to read up to the maxMessageSize + 1. + // We add one to the message size so that we can detect when a chunked request body + // is bigger than our configured max. + var readSize = (int?)req.ContentLength ?? (maxMessageSize + 1); + + var buffer = new byte[readSize]; + + // Read at least that many bytes from the body + var read = await body.ReadAtLeastAsync(buffer, readSize, throwOnEndOfStream: false); + + // We read more than the max, so this is a bad request + if (read > maxMessageSize) + { + // Message size exceeded + return Results.BadRequest(); + } + + // Attempt to send the buffer to the background queue. + if (queue.Writer.TryWrite(buffer.AsMemory(0..read))) + { + return Results.Accepted(); + } + + // We couldn't accept the message since we're overloaded + return Results.StatusCode(StatusCodes.Status429TooManyRequests); +}); + + +app.Run(); diff --git a/aspnetcore/fundamentals/minimal-apis/bindStreamPipeReader/7.0-samples/PipeStreamToBackgroundQueue/appsettings.Development.json b/aspnetcore/fundamentals/minimal-apis/bindStreamPipeReader/7.0-samples/PipeStreamToBackgroundQueue/appsettings.Development.json new file mode 100644 index 000000000000..0c208ae9181e --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/bindStreamPipeReader/7.0-samples/PipeStreamToBackgroundQueue/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/bindStreamPipeReader/7.0-samples/PipeStreamToBackgroundQueue/appsettings.json b/aspnetcore/fundamentals/minimal-apis/bindStreamPipeReader/7.0-samples/PipeStreamToBackgroundQueue/appsettings.json new file mode 100644 index 000000000000..10f68b8c8b4f --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/bindStreamPipeReader/7.0-samples/PipeStreamToBackgroundQueue/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/aspnetcore/fundamentals/minimal-apis/bindStreamPipeReader/7.0-samples/readme.txt b/aspnetcore/fundamentals/minimal-apis/bindStreamPipeReader/7.0-samples/readme.txt deleted file mode 100644 index 655bdceab454..000000000000 --- a/aspnetcore/fundamentals/minimal-apis/bindStreamPipeReader/7.0-samples/readme.txt +++ /dev/null @@ -1 +0,0 @@ -Create sample code here then delete this file. \ No newline at end of file