Record Request Body in ASP.NET Core 3.0–Attempt 2

on Monday, July 13, 2020

In the original post (Record Request Body in ASP.NET Core 3.0), the ITelemetryInitializer was creating some unexpected behavior. It was preventing the Operation ID associated with each request from changing/being updated. So, all the requests that were going through the system were being displayed on the same Performance graph. This created a nearly unusable performance graph as all the request were squished together and unreadable for timing purposes.

So, I needed to remove the ITelemetryInitializer from the code. But, I still wanted to record the JsonBody. The work around I used (which isn’t great) was to create a fake dependency on the request and record the body within the properties of the dependency.

Here’s the code:

using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.AspNetCore.Mvc.Filters;
namespace YourNamespace
{
public class TrackConstants
{
public const string JsonBody = "JsonBody";
}
public class TrackRequestBodyAttribute : ActionFilterAttribute
{
private readonly TelemetryClient _telemtry;
public TrackRequestBodyAttribute(
TelemetryClient telemetry = null
)
{
_telemtry = telemetry;
}
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var request = context.HttpContext.Request;
request.Body.Position = 0;
using var stream = new StreamReader(request.Body, Encoding.UTF8, false, 1024, true);
var body = await stream.ReadToEndAsync();
request.Body.Position = 0;
context.HttpContext.Items.Add(TrackConstants.JsonBody, body);
var toperation = _telemtry.StartNewOperation<DependencyTelemetry>("JsonBody");
toperation.TData.Add(TrackConstants.JsonBody, body);
toperation.Stop();
await base.OnActionExecutionAsync(context, next);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace YourNamespace
{
public static class IServiceCollectionExtensions
{
public static IServiceCollection AddYourDependencies(
this IServiceCollection services
)
{
// add other dependencies
services.TryAddTransient<TrackRequestBodyAttribute>();
return services;
}
}
}
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace YourNamespace
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddYourDependencies()
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMiddleware<EnableBufferingMiddleware>();
// use that middleware before everything else
}
}
}
view raw Startup.cs hosted with ❤ by GitHub
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
namespace YourNamespace
{
public static class HttpRequestExtensions
{
public static bool CanReadBody(this HttpRequest request)
{
return (
request.Method == HttpMethods.Post
|| request.Method == HttpMethods.Put
)
&& request.Body.CanRead;
}
}
public class EnableBufferingMiddleware
{
private readonly RequestDelegate _next;
public EnableBufferingMiddleware(
RequestDelegate next
)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var request = context.Request;
if (request.CanReadBody())
{
request.EnableBuffering();
}
await _next(context);
}
}
}
using System.Linq;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights;
using Microsoft.AspNetCore.Mvc;
namespace YourNamespace
{
[ApiController]
[Route("[controller]")]
public class ExampleController : ControllerBase
{
public CodePushedController() {}
[ServiceFilter(typeof(TrackRequestBodyAttribute))]
public async Task<IActionResult> PostAsync([FromBody] CodePushedRequest request)
{
// your code
}
}
}

0 comments:

Post a Comment


Creative Commons License
This site uses Alex Gorbatchev's SyntaxHighlighter, and hosted by herdingcode.com's Jon Galloway.