9

I tried this: Read request body twice and this: https://github.com/aspnet/Mvc/issues/4962 but did not work. I read request body like this:

app.Use(async (context, next) =>
{
    var requestBody = await ReadStream(context.Request.Body);
    var requestPath = context.Request.Path.ToString();
    //Do some thing

    await next.Invoke();

    var responseStatusCode = context.Response.StatusCode;
    //Do some other thing
});

private async Task<string> ReadStream(Stream stream)
{
    using (var streamReader = new StreamReader(stream))
    {
        var result = await streamReader.ReadToEndAsync();

        return result;
    }
}

In controller I get 'disposed object' or 'empty stream'.

Alireza Yavari
  • 121
  • 1
  • 7

5 Answers5

13

.netcore 3.1 version of @HoussamNasser's answer above. I have created a reusable function to read Request Body. Please note the change: HttpRequestRewindExtensions.EnableBuffering(request). EnableBuffering is now a part of HttpRequestRewindExtensions class.

public async Task<JObject> GetRequestBodyAsync(HttpRequest request)
    {
        JObject objRequestBody = new JObject();

        // IMPORTANT: Ensure the requestBody can be read multiple times.
        HttpRequestRewindExtensions.EnableBuffering(request);

        // IMPORTANT: Leave the body open so the next middleware can read it.
        using (StreamReader reader = new StreamReader(
            request.Body,
            Encoding.UTF8,
            detectEncodingFromByteOrderMarks: false,
            leaveOpen: true))
        {
            string strRequestBody = await reader.ReadToEndAsync();
            objRequestBody = SerializerExtensions.Deserialize<JObject>(strRequestBody);

            // IMPORTANT: Reset the request body stream position so the next middleware can read it
            request.Body.Position = 0;
        }

        return objRequestBody;
    }

This function will return a JObject which can used to read the properties of the Request Body object. SerializerExtensions is my custom extension for serializing & deserializing.

In the middleware, you can inject IHttpContextAccessor httpContextAccessor in the constructor. And then access the Request object like HttpRequest request = _httpContextAccessor.HttpContext.Request;. Finally, can call the reusable function like GetRequestBodyAsync(request)

3

After some more struggling and use "context.Request.EnableRewind()" it's finally worked like this:

app.Use(async (context, next) =>
{
    context.Request.EnableRewind();
    var stream = context.Request.Body;

    using (var reader = new StreamReader(stream))
    {
        var requestBodyAsString = await reader.ReadToEndAsync();

        if (stream.CanSeek)
            stream.Seek(0, SeekOrigin.Begin);

        //Do some thing

        await next.Invoke();

        var responseStatusCode = context.Response.StatusCode;
        //Do some other thing
    }
});
Alireza Yavari
  • 121
  • 1
  • 7
  • Have you tried this in .NET Core 3? I tried it but it seems to be not working anymore. – Jan Paolo Go Oct 16 '19 at 18:20
  • 5
    Yes, It doesn't work in dotnet core 3.0 There is another method: using Microsoft.AspNetCore.Http; then call 'context.Request.EnableBuffering()' @JanPaoloGo – Alireza Yavari Oct 30 '19 at 12:21
  • 1
    @AlirezaYavari I am facing the same problem while migrated to .net core 3.0. In .net core 3.0 I want to use BodyReader pipeline. I processed the request body for logging purpose, but i received validation error "The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0." Can you please help me. – Kamran Asim Nov 18 '19 at 06:02
  • 1
    In .netcore 3.1, EnableBuffering is a part of HttpRequestRewindExtensions class. This will not work when you try to access it from Request. I spent few hours in searching it, hope it helps the next reader. https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.httprequestrewindextensions.enablebuffering?view=aspnetcore-3.1 – Prateek Kumar Dalbehera Mar 30 '20 at 02:53
2

I was struggling around it, .net core 3.1 has breaking changes, "context.Request.EnableRewind();" is NO MORE available in .net core 3.1, more useful tips visit here

Working code in asp.net mvc api .net core 3.1 (startup.cs)

            app.Use(async (context, next) =>
        {
            context.Request.EnableBuffering();
            var stream = context.Request.Body;

            using (var reader = new StreamReader(stream))
            {
                var requestBodyAsString = await reader.ReadToEndAsync();

                if (stream.CanSeek)
                    stream.Seek(0, SeekOrigin.Begin);
               
                //some logging and other stuff goes here

                await next.Invoke();

            }
        });
Surya-AIS
  • 165
  • 4
1

It is probably because of the using statement around your StreamReader. Using disposes the StreamReader which calls dispose on the underlying stream. See the answer here for more details. You could try keeping a reference and disposing the StreamReader after await next.Invoke(); completes.

Sanjid
  • 85
  • 1
  • 5
1

When stream is read second time then the stream pointer is set to the last position. You should try to move it back to position zero to read it again from the beginning.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NLog;

namespace Test_Middlewares.Middlewares
{
    // You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
    public class LoggingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<LoggingMiddleware> _logger;

        public LoggingMiddleware(RequestDelegate next, ILogger<LoggingMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task InvokeAsync(HttpContext httpContext)
        {
            var HttpContextBody = httpContext.Request.Body;
            string requestBody = "";

            httpContext.Request.EnableBuffering();

            // Leave the body open so the next middleware can read it.
            using (var reader = new StreamReader(
                httpContext.Request.Body,
                encoding: Encoding.UTF8,
                detectEncodingFromByteOrderMarks: false,
                bufferSize: -1,
                leaveOpen: true))
            {
                var body = await reader.ReadToEndAsync();
                // Do some processing with body…

                // Reset the request body stream position so the next middleware can read it
                httpContext.Request.Body.Position = 0;
            }

            _logger.LogDebug("Middleware 1 body =" + requestBody);

            await _next.Invoke(httpContext);
        }
    }

    // Extension method used to add the middleware to the HTTP request pipeline.
    public static class LoggingMiddlewareExtensions
    {
        public static IApplicationBuilder UseLoggingMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<LoggingMiddleware>();
        }
    }
}

For more info please refer to these links :

https://devblogs.microsoft.com/aspnet/re-reading-asp-net-core-request-bodies-with-enablebuffering/

https://gunnarpeipman.com/aspnet-core-request-body/