Search code examples
asp.net-corehttpmiddleware

Prevent middleware from reading request body


In my ASP.NET 6 application I have an endpoint like this where I want to read the request body stream directly:

[HttpPost("{id}/Binary")]
[OpenApiBodyParameter("application/octet-stream")]
public async Task UploadBinary([FromRoute] Guid id, [FromHeader(Name = "Content-Length")] long contentLength)
{
    // Trying to read Request.Body stream here fails with 
    // "Unexpected end of request content" exception.

    // My real code looks different, but trying to read just
    // one byte can reproduce the problem:
    var buffer = new byte[1];
    await Request.Body.ReadAsync(buffer, 0, 1);
}

Reading the request body gives a "Unexpected end of request content" exception.

[08:51:27 ERR] An unhandled exception has occurred while executing the request.
Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Unexpected end of request content.
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1ContentLengthMessageBody.ReadAsyncInternal(CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpRequestStream.ReadAsyncInternal(Memory`1 destination, CancellationToken cancellationToken)
   at ... (my own action)

I'm pretty sure this is because some middleware reads the request body so that I cannot read it (again) in my code.

How can I figure out which middleware is causing that unwanted read? (and avoid it?)

This is how I set up the request pipeline:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
        app.UseDeveloperExceptionPage();

    app.UseSerilogRequestLogging();

    app.UseStaticFiles();
    if (!env.IsDevelopment())
        app.UseSpaStaticFiles();

    app.UseOpenApi();
    app.UseSwaggerUi3(c => {
        c.OAuth2Client = new NSwag.AspNetCore.OAuth2ClientSettings() { ClientId = null, ClientSecret = null };
    });

    app.UseCors(builder => builder.WithOrigins("localhost", "http://localhost:4200", "http://localhost:4210")
        .AllowAnyHeader().AllowAnyMethod().AllowCredentials());

    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller}/{action=Index}/{id?}");
    });

    app.UseSpa(spa =>
    {
        // To learn more about options for serving an Angular SPA from ASP.NET Core,
        // see https://go.microsoft.com/fwlink/?linkid=864501

        spa.Options.SourcePath = "ClientApps/manage-app";

        if (env.IsDevelopment() && !env.IsEnvironment("RunLocal"))
        {
            spa.UseAngularCliServer(npmScript: "start");
        }
    });
}

My suspicion was that UseSerilogRequestLogging could cause that unwanted read of the request body, but commenting it out does not solve my issue.

What else could cause my problem?


Solution

  • Solved!

    The root cause was actually on the client side (HttpClient being disposed too early).