In a .Net 6 Web API using NLog, how can I intercept the posted body so I can manipulate it before logging it?
Here's what I've got:
I've got a .Net Framework 4.7.2 Web API with a DelegatingHandler to intercept a request's body. I then go through each key and replace the value if the key is in a list of strings. For example: if the key is Password or Pwd I replace the value with xxx so that I can log the full body without security concerns. it's worked very well. I'm trying to replicate this on a .Net 6 Web API Project.
Here's what I've done:
I've looked through the NLog source code to see how they get the aspnet-request-posted-body. I believe I've got something but I don't see NLog calling/using my RenderLayout.
[LayoutRenderer("aspnet-request-posted-body-2")]
public class TestRender : AspNetLayoutRendererBase {
protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent) {
var items = HttpContextAccessor.HttpContext?.Items;
if (items == null || items.Count == 0) {
return;
}
if (items.TryGetValue("aspnet-request-posted-body-2", out var value)) {
builder.Append(value as string);
}
}
}
My Middleware
public class RequestLoggerMiddleware {
private readonly RequestDelegate _next;
private readonly ILogger<RequestLoggerMiddleware> _logger;
public RequestLoggerMiddleware(RequestDelegate next, ILogger<RequestLoggerMiddleware> logger) {
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext context) {
var requestBody = await new HttpContextExtensions().PeekBodyAsync(context);
var contentType = context.Request.ContentType;
// Not done yet, but adding this for testing
switch (contentType) {
case "application/x-www-form-urlencoded":
// Clean data
...
// Add to context
context.Items.Add("aspnet-request-posted-body-2", requestBody);
break;
case "application/*+json":
case "application/json":
case "text/json":
// Clean data
...
// Add to context
context.Items.Add("aspnet-request-posted-body-2", JsonSerializer.Serialize(requestBody));
break;
default:
break;
}
_logger
.LogInformation("TESTING");
// With a breakpoint I checked that `content` does have the item listed above
await _next.Invoke(context);
}
}
Here's is my Program.cs file
var logger = LogManager
.LoadConfiguration(string.Concat(Directory.GetCurrentDirectory(), "/Configs/NLog/nlog.config"))
.Setup()
.SetupExtensions(x => {
x.RegisterLayoutRenderer<TestRender>("aspnet-request-posted-body-2");
})
.GetCurrentClassLogger();
logger.Debug("init main");
try {
var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.Host.UseNLog();
var app = builder.Build();
// Register NLog's Request Body Layout
app.UseMiddleware<NLogRequestPostedBodyMiddleware>();
// Register my custom layout
app.UseMiddleware<RequestLoggerMiddleware>();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
} catch (Exception exception) {
logger.Error(exception, "Stopped program because of exception");
throw;
} finally {
LogManager.Shutdown();
}