Asp.Net core ActionFilter context request body disposed

Viewed 1247

I have a asp.net core web api project in which I created an action filter to log every call made to the api

public class RequestLoggerActionFilter : ActionFilterAttribute
{
    private readonly ILogger _logger;

    public RequestLoggerActionFilter(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger("RequestLogger");
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        var request = context.HttpContext.Request;
        request.Body.Position = 0;

        using (var reader = new StreamReader(request.Body))
        {
            var bodyString = reader.ReadToEnd();
            _logger.LogInformation($"API call recevied on {request.Method.ToUpper()} {request.Path} RequestBody: {bodyString}");
        }

        base.OnActionExecuting(context);
    }
}

This piece of code works ok in most case. I get problem when my controller method return a 201 code using CreatedAtAction method

return CreatedAtAction("Get", new { Id= myObject.Id });

the problem is, when I hit my controller method which creates an object, it enters a first time in my actionfilter and logs the request correctly. Then, when the return line is reached , it hits my actionfilter a second time (I'm not sure why a second request is send, I guess it's kind of doing the same thing as a Redirect). The second time OnActionExecuting of my actionFilter is executed, this line of code throws an exception

request.Body.Position = 0;

'Cannot access a disposed object.'

Body of my request is disposed. My ActionFilter is registered this way in Startup.cs file

services.AddMvc(c =>
                {
                    c.Filters.Add(typeof(RequestLoggerActionFilter));
                }
            );

The lifecycle of my actionFilter is Scoped, I also tried Transient, but it doesn't fix the problem.

services.AddScoped<RequestLoggerActionFilter>();
1 Answers

I made a test with request.EnableRewind(); and code below, it works with CreatedAtAction.

    public class RequestLoggerActionFilter : ActionFilterAttribute
{
    private readonly ILogger _logger;

    public RequestLoggerActionFilter(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger("RequestLogger");
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        var request = context.HttpContext.Request;
        request.EnableRewind();
        request.Body.Position = 0;

        using (var reader = new StreamReader(request.Body))
        {
            var bodyString = reader.ReadToEnd();
            _logger.LogInformation($"API call recevied on {request.Method.ToUpper()} {request.Path} RequestBody: {bodyString}");
        }

        base.OnActionExecuting(context);
    }
}
Related