Search code examples
c#iisloggingstreamkestrel

Reading HTTP request body in HttpModule


I have a .NET Core Web API running in IIS. I want to add the request body in the IIS log on POST. So in .NET Full Framework I've created class library with a single class that implements IHttpModule. I have to use .NET Full Framework because IHttpModule is not in Core.

My problem is that the request InputStream can only be read once. So when I read it in my code, the stream is already at it's end (and the Stream object has been modified with) when it hits my Core API. I've tried all I can think of and I've searched a lot.

What doesn't work:

var bytes = new byte[request.InputStream.Length];
request.InputStream.Read(bytes, 0, bytes.Length);
request.InputStream.Position = 0;
var content = Encoding.UTF8.GetString(bytes);

Yes I get the body just fine, but the stream position isn't reset. Resetting the position doesn't help.

var reader = new StreamReader(HttpContext.Current.Request.InputStream, Encoding.UTF8, true, 1024, true);
HttpContext.Current.Request.InputStream.Position = 0;

The last flag should tell the stream to keep it open, but the call to the Core still fails.

context.HttpContext.Request.Body

Isn't available in standard .NET.

Then I tried this:

var s2 = new MemoryStream(request.TotalBytes);
HttpContext.Current.Request.InputStream.CopyTo(s2);

Still fails. Even this small code makes it fail:

var v = request.TotalBytes;

My thoughts so far

Looking at the sourcecode for the stream I can see that some of it's internal data is changed when properties are read, so what I'm thinking is that it's IIS making sure that the message hasn't been tampered with before sending it to Kestrel. Can anyone shed some light on this? Maybe you know of another do accomplish this task?

Side note: Yes I can get the body message out in other ways using other IIS modules, but they don't put it in the standard IIS log, they create their own. I'm already consuming the IIS log into my ELK stack, that's why it would just be easier this way.


Solution

  • a little bit late, but I was dealing with this several hours too. Finally got the solution using

    request.GetBufferedInputStream()
    

    instead

    request.InputStream
    

    (.NET Framework 4.6)