.NET API: capture original message body

Sometimes you just need to record the original message that was submitted to your API. My colleague Onno Pierik and I encountered such an event. We needed to record the original submitted SOAP message (body) and submit it (under certain conditions) to another service. To be honest: most of the scenarios I've seen so far end up with memory problems, so use with caution!

Recording middleware

We've found a nice piece of code that can replace the original body of a request with a new memory stream:

//Reload the body to ensure we have the full message
var memoryStream = new MemoryStream((int)httpContext.Request.ContentLength.GetValueOrDefault(1024));
await httpContext.Request.Body.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Seek(0, SeekOrigin.Begin);
httpContext.Request.Body = memoryStream;

Let's turn the code into a piece of middleware:

using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

public class RecorderMiddleware 
{
    private readonly RequestDelegate _next;

    public RecorderMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext httpContext)
    {
        var ms = new MemoryStream((int)httpContext.Request.ContentLength.GetValueOrDefault(1024));
            
        await httpContext.Request.Body.CopyToAsync(ms).ConfigureAwait(false);
        ms.Seek(0, SeekOrigin.Begin);

        httpContext.Items.Add("recording", Encoding.UTF8.GetString(ms.ToArray()));
        ms.Seek(0, SeekOrigin.Begin);

        httpContext.Request.Body = ms;

        await _next(httpContext);
    }
}

We save the recording as an UTF-8 string into the HttpContext object for later use.

Startup

We need to set up 2 things in our Startup class. The first thing is the ability for a controller to access the HTTP accessor. The second thing is to add the RecorderMiddleware to the HTTP pipeline. Make sure to add it to the proper place, before any controller has accessed the request.

public void ConfigureServices(IServiceCollection services)
{
	// { ... } rest of the services
	services.AddHttpContextAccessor();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
	app.UseMiddleware<RecorderMiddleware>();
	// { ... } rest the middleware
}

Retrieval

When you inject an IHttpContextAccessor httpContextAccessor into your controller, you can retrieve the recorded message by the following code:

string recording = "";

if(httpContextAccessor.HttpContext.Items.TryGetValue("recording", out var r))
{
	recording = (string) r;
}

Final thoughts

Be careful on what to record and what not to record. Some messages might be too big for a string or cause other problems.

expand_less