ASP.NET Core: log all requests (reading body twice) EnableRewind not working for [FromBody] attribute

Viewed 2456

I am following this post how to log all requests.

But this code:

public async Task Invoke(HttpContext context)
{
    await this.LogRequest(context.Request);

    await this._next(context);
}

private async Task LogRequest(HttpRequest request)
{
    var body = request.Body;
    request.EnableRewind();

    var buffer = new byte[Convert.ToInt32(request.ContentLength)];
    await request.Body.ReadAsync(buffer, 0, buffer.Length);
    request.Body.Seek(0, SeekOrigin.Begin);
    var bodyAsText = Encoding.UTF8.GetString(buffer);
    request.Body = body;
}

doesn't work, because I get

"A non-empty request body is required."

in Controller with [ApiController] attribute

[HttpPut("MyMethod")]
public async Task<ActionResult<MyModel>> MyMethod([FromBody]MyArgs model)
{
    //...
}

I am trying to do all sorts of combinations

    string bodyAsText = null;
    if (request.ContentLength > 0)
    {
        var body = request.Body;
        request.EnableRewind();

        using (var streamReader = new StreamReader(request.Body, Encoding.UTF8))
        {
            bodyAsText = await streamReader.ReadToEndAsync();
            request.Body.Seek(0, SeekOrigin.Begin);
        }
        request.Body = body;
    }

but nothing works. What am I missing here? There is some answer with similar solution on SO, but I don't understand why it doesn't work in my case.

If I comment code for reading body, it works.

1 Answers

I was waiting for Tseng to post an answer as he already point me to a problem and solution in comment. But since he didn't do it, I will try to answer based on comment and debugging experience.

As Tseng mention EnableRewind should be called before var body = request.Body;

This code:

if (request.ContentLength > 0)
{
    request.EnableRewind();
    var body = request.Body;

    using (var streamReader = new StreamReader(request.Body, Encoding.UTF8))
    {
         bodyAsText = await streamReader.ReadToEndAsync();
         request.Body.Seek(0, SeekOrigin.Begin);
    }
    request.Body = body;
}

throws error:

Cannot access a disposed object. Object name: 'FileBufferingReadStream'.

in line await this._next(context);.

Working code is:

if (request.ContentLength > 0)
{
    request.EnableRewind();
    var body = request.Body;

    var buffer = new byte[Convert.ToInt32(request.ContentLength)];
    await request.Body.ReadAsync(buffer, 0, buffer.Length);
    request.Body.Seek(0, SeekOrigin.Begin);
    bodyAsText = Encoding.UTF8.GetString(buffer);
    request.Body = body;
}

Thank you @Tseng.

Related