Search code examples
c#httprequestminimal-apis

Do I need context.Request.EnableBuffering?


To be clear: I want the HTTP headers AND body in case the request is invalid...

I have an extreme minimal Web API in C# which looks like this:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app
    .UseHsts()
    .UseHttpsRedirection()
    .UseDefaultFiles()
    .UseStaticFiles();
var Data = (string data ) => WeatherUpdateData.DeserializeFromJson(File.ReadAllText(data));
app
    .Use(async (context, next) =>
    {
        if(context.Request.Method.Equals("GET", StringComparison.InvariantCultureIgnoreCase) && !context.Request.Path.Value!.Contains("."))
        {
            await next();
        }
        else
        {
            context.Response.StatusCode = (int)HttpStatusCode.GatewayTimeout;
            // <<<<< Write raw request to log. >>>>>
        }
    });
app.MapGet("/text", (IConfiguration configuration, IWebHostEnvironment env) =>
{
    var data = Data(configuration["Data:UpdateFile"]);
    return Results.File(data.Weather, "text/plain", $"{data.Id}.txt");
});
app.MapGet("/sound", (IConfiguration configuration, IWebHostEnvironment env) =>
{
    var data = Data(configuration["Data:UpdateFile"]);
    return Results.File(data.Sound, "audio/mpeg", $"{data.Id}.mp3");
});
app.MapGet("/image", (IConfiguration configuration, IWebHostEnvironment env) =>
{
    var data = Data(configuration["Data:UpdateFile"]);
    return Results.File(data.Image, "image/png", $"{data.Id}.png");
});
app.MapGet("/data", (IConfiguration configuration, IWebHostEnvironment env) =>
{
    var data = Data(configuration["Data:UpdateFile"]);
    return Results.File(data.Json, "application/json", $"{data.Id}.json");
});
app.Run();

What it does is simple. There are 4 methods that are linked to a bunch of data. This data is made available in raw form, or as text, image or sound. The Data:UpdateFile links to a JSON file containing the file locations of four files and these files are updated every day. (Basically, every day a new folder gets created by a separate console application, with 4 new files in it before the UpdateFile is updated.)
Now, this Web API is limited to these four actions plus the static files. However, the first app.Use adds a filter to check if the user is trying to call something else. For example, if they try to put or post to this service or starts exploring for other routes. Those requests get blocked by this code and they get a gateway timeout as error.
Which works! The static pages will pass through and the site is working fine. (Yes, a weather report.) But when someone tries to e.g. access index.php or admin.aspx then they get a 504 error. Perfect! :)
But when this happens, I also want the original request recorded in a log. This would have to be the whole, raw request as it was received. And while Google helps a lot in drowning me with possible answers, including here at Stack Overflow, I just fail to find a solution that works with a minimal Web API like this.
Now, I can use context.Request.EnableBuffering(); in the app.Use() call but do I need to, as it will bypass all the routes? It seems to me that I don't, as nothing is going to process the request in this part.
So, all I need to know is how to get the raw request headers and body in this app.Use() method so I can just dump it in a log database for future evaluations. (And keep track of attacks on this server.)


Solution

  • Terminal middleware is permitted to consume the request without having to buffer it. See https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-6.0