ActionFilterAttribute: HttpContext.Request.Body always empty

600 Views Asked by At

I am trying to get the request body in an ActionFilterAttribute.

The JSON value is always "". I am using .NET7.0 and I also added below code to Program.cs

.Use(async (context, next) =>
    {
        try
        {
            context.Request.EnableBuffering();
            await next();
        }
        catch (Exception e)
        {
        }
         .
         .
         .

The attribute code snippet:

 public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {

        var expectedModel = context.ActionArguments.Values.FirstOrDefault();
        if (expectedModel == null)
        {
            context.Result = new BadRequestObjectResult("Model empty.");
            return;
        }

        var requestBodyStream = new MemoryStream();
        await context.HttpContext.Request.Body.CopyToAsync(requestBodyStream);
        requestBodyStream.Seek(0, SeekOrigin.Begin);
        var json = await new StreamReader(requestBodyStream).ReadToEndAsync();
}

RequestBody

My controller method:

    [HttpPost("scoring/add")]
    [JsonModelValidationAttribute]
    public async Task<IActionResult> AddScoringResult([FromBody] ScoreModel model)
    {
      //I am going to add logic.
    }

I want to get the request body and compare the ScoreModel to check for any misspelled property. How to get the body from the request?

Classes and example:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

public class JsonModelValidationAttribute : ActionFilterAttribute
{
    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {

        var expectedModel = context.ActionArguments.Values.FirstOrDefault();
        if (expectedModel == null)
        {
            context.Result = new BadRequestObjectResult("Model empty.");
            return;
        }

        var requestBodyStream = new MemoryStream();
        await context.HttpContext.Request.Body.CopyToAsync(requestBodyStream);
        requestBodyStream.Seek(0, SeekOrigin.Begin);
        var json = await new StreamReader(requestBodyStream).ReadToEndAsync();
        Console.WriteLine(json);
    }
}

ScoreModel

using System;
using System.Collections.Generic;
using System.Security.Principal;

namespace Scoring.Data.Custom;

public class ScoreModel
{
    public int Id { get; set; }


    public int Score { get; set; }

    public string Name { get; set; }
}

Controller

using Scoring.Data.Custom;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace Scoring.API.Custom;

[ApiController]
public class ScoringController : ControllerBase
{

    public ScoringController()
    {

    }

    [HttpPost("scoring/add")]
    [JsonModelValidationAttribute]
    public async Task<IActionResult> AddScoringResult([FromBody] ScoreModel model)
    {

        return Ok(model);
    }
}
1

There are 1 best solutions below

0
Guru Stron On

After enabling buffering (do not forget that it should be placed early in the pipeline) you need to not forget to rewind the incoming stream (depending where along the pipeline you are you might need to rewind it before and/or after reading it). In this particular case the following will fix the issue:

public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
    // ...
    context.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin); // <--- here
    
    var requestBodyStream = new MemoryStream();
    await context.HttpContext.Request.Body.CopyToAsync(requestBodyStream);
    requestBodyStream.Seek(0, SeekOrigin.Begin);
    var json = await new StreamReader(requestBodyStream).ReadToEndAsync();
    Console.WriteLine(json);
}

Also I would recommend to dispose the created stream and do not forget to call await next():

public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
    var expectedModel = context.ActionArguments.Values.FirstOrDefault();
    if (expectedModel == null)
    {
        context.Result = new BadRequestObjectResult("Model empty.");
        return;
    }

    var request = context.HttpContext.Request;
    request.Body.Seek(0, SeekOrigin.Begin);
    using (var reader = new StreamReader(request.Body, leaveOpen: true))
    {
        var json = await reader.ReadToEndAsync();

        Console.WriteLine(json);
    }

    request.Body.Seek(0, SeekOrigin.Begin);
    await next();
}