Search code examples
c#asp.net-coreaudit.net

Prevent Audit.Net from formatting BodyContent values as strings?


I'm getting familiar with audit.net, and finding that the serialization isn't working quite right. I'm wondering if I need to add more configuration. My current config is as follows:

// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    // ... configuration here
    Audit.Core.Configuration.Setup().UseFileLogProvider("./logs"); // issue also happens when using SqlServer
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.Use(async (context, next) =>
    {
        context.Request.EnableBuffering();
        await next();
    });

    app.UseAuditMiddleware(_ => _
        .IncludeHeaders()
        .IncludeResponseHeaders()
        .IncludeRequestBody()
        .IncludeResponseBody(ctx => ctx.Response.StatusCode == 200));

    app.UseEndpoints(endpoints => 
    {
        endpoints.MapControllers();
    });
}

What I get (string with escape chars):

"ResponseBody": {
  "Type": "application/json; charset=utf-8",
  "Value": "[{\"date\":\"2021-04-22T12:39:31.173043-06:00\",\"temperatureC\":4,\"temperatureF\":39,\"summary\":\"Balmy\"},{\"date\":\"2021-04-23T12:39:31.1734617-06:00\",\"temperatureC\":34,\"temperatureF\":93,\"summary\":\"Balmy\"},{\"date\":\"2021-04-24T12:39:31.1734642-06:00\",\"temperatureC\":-8,\"temperatureF\":18,\"summary\":\"Cool\"},{\"date\":\"2021-04-25T12:39:31.1734646-06:00\",\"temperatureC\":32,\"temperatureF\":89,\"summary\":\"Mild\"},{\"date\":\"2021-04-26T12:39:31.1734647-06:00\",\"temperatureC\":-2,\"temperatureF\":29,\"summary\":\"Mild\"}]"
},

What I hope to get (objects):

"ResponseBody": {
  "Type": "application/json; charset=utf-8",
  "Value": [
    {
      "date": "2021-04-22T12:39:31.173043-06:00",
      "temperatureC": 4,
      "temperatureF": 39,
      "summary": "Balmy"
    },
    {
      // more temperatures
    },
    ...,
  ]

Solution

  • That's because the middleware can only access the response body already serialized as a byte array, so it's only decoded as string.

    On the other hand, the action filter can access the object before it's written to the response stream, so if you also add it to the pipeline, the audit event for the requests reaching an action method, will look like what you're expecting.

    For example, on your Startup class:

    using Audit.WebApi;
    
    public void ConfigureServices(IServiceCollection services)
    {
        services
            .AddMvc(mvcOptions =>
            {
                mvcOptions.AddAuditFilter(a => a
                    .LogAllActions()
                    .IncludeResponseHeaders()
                    .IncludeRequestBody()
                    .IncludeResponseBody());
            });
    }
    

    Check this for more information about having both the middleware and the action filter configured.

    If you don't want or can't add the action filter, you could modify the audit event before it's saved with a custom action. For example, also on your Startup class:

    using Newtonsoft.Json;
    
    Audit.Core.Configuration.AddOnSavingAction(scope =>
    {
        var audit = scope.GetWebApiAuditAction();
        if (audit?.ResponseBody?.Type.Contains("application/json") == true)
        {
            audit.ResponseBody.Value = JsonConvert.DeserializeObject(audit.ResponseBody.Value.ToString());
        }
    });