Read request body twice

后端 未结 3 1722
傲寒
傲寒 2020-12-03 13:30

I am trying to read the body in a middleware for authentication purposes, but when the request gets to the api controller the object is empty as the body has already been re

相关标签:
3条回答
  • 2020-12-03 13:59

    If you're using application/x-www-form-urlencoded or multipart/form-data, you can safely call context.Request.ReadFormAsync() multiple times as it returns a cached instance on subsequent calls.

    If you're using a different content type, you'll have to manually buffer the request and replace the request body by a rewindable stream like MemoryStream. Here's how you could do using an inline middleware (you need to register it soon in your pipeline):

    app.Use(next => async context =>
    {
        // Keep the original stream in a separate
        // variable to restore it later if necessary.
        var stream = context.Request.Body;
    
        // Optimization: don't buffer the request if
        // there was no stream or if it is rewindable.
        if (stream == Stream.Null || stream.CanSeek)
        {
            await next(context);
    
            return;
        }
    
        try
        {
            using (var buffer = new MemoryStream())
            {
                // Copy the request stream to the memory stream.
                await stream.CopyToAsync(buffer);
    
                // Rewind the memory stream.
                buffer.Position = 0L;
    
                // Replace the request stream by the memory stream.
                context.Request.Body = buffer;
    
                // Invoke the rest of the pipeline.
                await next(context);
            }
        }
    
        finally
        {
            // Restore the original stream.
            context.Request.Body = stream;
        }
    });
    

    You can also use the BufferingHelper.EnableRewind() extension, which is part of the Microsoft.AspNet.Http package: it's based on a similar approach but relies on a special stream that starts buffering data in memory and spools everything to a temp file on disk when the threshold is reached:

    app.Use(next => context =>
    {
        context.Request.EnableRewind();
    
        return next(context);
    });
    

    FYI: a buffering middleware will probably be added to vNext in the future.

    0 讨论(0)
  • 2020-12-03 14:08

    This works with .Net Core 2.1 and higher.

    Today I run in a similar issue. Long story short, what used to work with

    Body.Seek(0, SeekOrigin.Begin);
    

    resulted in today in exception, at least in my case. This happened after the code was migrated to the latest version of .NET Core.

    The workaround for me was to add this:

    app.Use(next => context => { context.Request.EnableBuffering(); return next(context);
    

    Add this before setting up controllers or MVC. This seems to be added as part of the .NET Core 2.1 version.

    Hope this helps someone!

    Cheers and happy coding.

    0 讨论(0)
  • 2020-12-03 14:19

    Usage for PinPoint's mention of EnableRewind

    Startup.cs
    using Microsoft.AspNetCore.Http.Internal;
    
    Startup.Configure(...){
    ...
    //Its important the rewind us added before UseMvc
    app.Use(next => context => { context.Request.EnableRewind(); return next(context); });
    app.UseMvc()
    ...
    }
    

    Then in your middleware you just rewind and reread

    private async Task GenerateToken(HttpContext context)
        {
         context.Request.EnableRewind();
         string jsonData = new StreamReader(context.Request.Body).ReadToEnd();
        ...
        }
    
    0 讨论(0)
提交回复
热议问题