Is there a way to get the request body in the .NET Core FilterAttribute?

Sample of my request

http://localhost:8065/api/note
POST
content-type:application/json
request body: { "id" : "1234", "title" : "test", "status" : "draft"}

      

and the answer should be

{ "msg" : "ok", "code" : 1}

      

Act

public async Task<IActionResult> Post([FromBody]NoteModel model)

      


To ensure that every request is automatically logged, I create an attribute to accomplish this task. The attribute looks like this: (from Microsoft Docs )

public class SampleActionFilterAttribute : TypeFilterAttribute
{
    public SampleActionFilterAttribute():base(typeof(SampleActionFilterImpl))
    {
    }

    private class SampleActionFilterImpl : IActionFilter
    {
        private readonly ILogger _logger;
        public SampleActionFilterImpl(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<SampleActionFilterAttribute>();
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {

        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            _logger.LogDebug("[path]" + context.HttpContext.Request.Path);
            _logger.LogDebug("[method]" + context.HttpContext.Request.Method);
            _logger.LogDebug("[body]"); //log request body, expectation: { "id" : "1234", "title" : "test", "status" : "draft"}
            _logger.LogDebug("[statuscode]" + context.HttpContext.Response.StatusCode);
            _logger.LogDebug("[response]"); //log response
        }
    }
}

      

I am trying to use streamReader to get request body just to get empty string.

StreamReader reader = new StreamReader(context.HttpContext.Request.Body);
string text = reader.ReadToEnd();

      

Is it because the body was read [fromBody] from the controller, so the stream cannot be read twice? If so, how should I get the request body and response in the method OnActionExecuted

?


Update:

I just copied the Set code into my project but didn't work. Here is a debug gifenter image description here

+3


source to share


2 answers


According to this " Best way to log / read request body in middleware ", the following should work:

// using Microsoft.AspNetCore.Http.Internal;

public class SampleActionFilterAttribute : TypeFilterAttribute
{
    ... 

    public void OnActionExecuting(ActionExecutedContext context)
    {
        // read body before MVC action execution
        string bodyData = ReadBodyAsString(context.HttpContext.Request);
    }

    private string ReadBodyAsString(HttpRequest request)
    {
        var initialBody = request.Body; // Workaround

        try
        {
            request.EnableRewind();

            using (StreamReader reader = new StreamReader(request.Body))
            {
                string text = reader.ReadToEnd();
                return text;
            }
        }
        finally
        {
            // Workaround so MVC action will be able to read body as well
            request.Body = initialBody; 
        }

        return string.Empty;
    }
 }

      

Also a similar approach as described in Requesting SO post request body twice




Update : The above approach in ReadBodyAsString

c will work if used in middleware and not in an action filter. The difference is that when the action filter is called (even for OnActionExecuting

), the body stream has already been read and [FromBody] model

populated.

The good thing is that you can get the model directly in the action filter using context.ActionArguments["<model_name>"]

. In your case:

public void OnActionExecuted(ActionExecutedContext context)
{
   var model = context.ActionArguments["model"] as NoteModel;
}

      

+4


source


If you are using IActionResult in your controllers and want .NET objects, you can write a filter like this:

public class SampleFilter : IActionFilter
{
    public void OnActionExecuted(ActionExecutedContext context)
    {
        if (context.Result is ObjectResult)
        {
            var objResult = (ObjectResult)context.Result;
        }
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {

    }
}

      

The moment it hits OnActionExecuted, the ObjectResult task has already completed, so you can just retrieve the value. You can also get StatusCode with objResult.StatusCode.



In the controller, return Ok (...) actually creates OkObjectResult, etc.

If you specifically want to get a serial result, then set the answer more right.

0


source







All Articles