Reading request body in middleware for .Net 5
Asked Answered
I

2

12

I am trying to read the request body that was sent from client to my backend. The content is sent in JSON format and has users input from a form. How do I read the request body in a middleware that I set up for my controller Route.

Here is my Model

namespace ChatboxApi.Models
{
    public class User
    {
        public string Login { get; set; } = default;
        public string Password { get; set; } = default;
        public string Email { get; set; } = default;
        public string FullName { get; set; } = default;
        public int Phone { get; set; } = default;
        public string Website { get; set; } = default;

    }
}

Here is my Controller

namespace ChatboxApi.Controllers
{
    [ApiController]
    [Route("api/signup")]
    public class SignupController : ControllerBase
    {
        [HttpPost]
        public ActionResult SignUp([FromBody] User user)
        {
            return Ok(user);
        }

    }
}

Here is my middleware class

namespace ChatboxApi.Middleware
{
    public class CreateSession
    {
        private readonly RequestDelegate _next;

        public CreateSession(RequestDelegate next)
        {
            this._next = next;
        }

        public async Task Invoke(HttpContext httpContext)
        {
           //I want to get the request body here and if possible
           //map it to my user model and use the user model here.
            
            
        }

}

Icsh answered 31/5, 2021 at 6:33 Comment(4)
You shouldn't be using Middleware for something like this, imo. Your request routing should ideally depend only on the request path - or at most on the HTTP request headers. You should not route based on the contents of the request body. That's just asking for trouble - not least because ASP.NET will route requests before the request body has even been received - which explains the problems you're having.Cronk
So you are saying it is better to do the work in the controller than in the middleware?Icsh
Yes. "Work" should not be done in middleware - middleware should be used to set up the request-handling environment or to provide services to the actual handlers (e.g. set up failed-request logging, decompressing gzip-compressed request bodies, etc).Cronk
@Rena Thanks for reminding me. Your answer did work and it is been marked as the answer to the question.Icsh
C
39

Usually Request.Body does not support rewinding, so it can only be read once. A temp workaround is to pull out the body right after the call to EnableBuffering and then rewinding the stream to 0 and not disposing it:

public class CreateSession
{
    private readonly RequestDelegate _next;

    public CreateSession(RequestDelegate next)
    {
        this._next = next;
    }

    public async Task Invoke(HttpContext httpContext)
    {
        var request = httpContext.Request;
        if (request.Method == HttpMethods.Post && request.ContentLength > 0)
        {
            request.EnableBuffering();
            var buffer = new byte[Convert.ToInt32(request.ContentLength)];
            await request.Body.ReadAsync(buffer, 0, buffer.Length);
            //get body string here...
            var requestContent = Encoding.UTF8.GetString(buffer);

            request.Body.Position = 0;  //rewinding the stream to 0
        }
        await _next(httpContext);

    }
}

Register the service:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    //...
    app.UseHttpsRedirection();

    app.UseMiddleware<CreateSession>();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });

}
Corse answered 31/5, 2021 at 9:12 Comment(2)
This approach won't work if the request body exceeds 2GB - or if there is no request body.Cronk
In .NET 6, request.Body.Position needs to be reset prior to reading content, it's possible that other middleware have already read the body and it is no longer at position 0.Ushaushant
I
-6

I created middleware to log every request and save in database. I hope this code help you.

using Newtonsoft.Json;

namespace ChatboxApi.Middleware
{
    public class CreateSession
    {
        private readonly RequestDelegate _next;    

        public CreateSession(RequestDelegate next)
        {
            this._next = next;
        }
           
        public async Task Invoke(HttpContext httpContext)
        {
           // I want to get the request body here and if possible
           // map it to my user model and use the user model here.

            var req = httpContext.Request;
            var xbody = await ReadBodyAsync(req);
            var bodyRaw = JsonConvert.SerializeObject(xbody);

            var param = JsonConvert.SerializeObject(req.Query);
            var header = JsonConvert.SerializeObject(req.Headers);
            var originalUrl = JsonConvert.SerializeObject(req.Path);
        }
}
Inferior answered 17/12, 2021 at 5:59 Comment(1)
What is the content of ReadBodyAsync method?Ribose

© 2022 - 2024 — McMap. All rights reserved.