4

Is it possible to view request's response body in Application Insights? I have seen many questions/articles regarding Request body but found none directing about Response body.

I am building a MVC core 2.1 Web Api.

Related article:

View POST request body in Application Insights

Here is my code, getting exception while creating reader from stream i.e. "Stream was not readable.".

public class ResponseBodyInitializer : ITelemetryInitializer
{
    readonly IHttpContextAccessor httpContextAccessor;

    public ResponseBodyInitializer(IHttpContextAccessor httpContextAccessor)
    {
        this.httpContextAccessor = httpContextAccessor;
    }

    public void Initialize(ITelemetry telemetry)
    {
        if (telemetry is RequestTelemetry requestTelemetry)
        {
            HttpContext httpContext = httpContextAccessor.HttpContext;
            HttpRequest request = httpContext.Request;
            HttpResponse response = httpContext.Response;

            if (request.Method == HttpMethods.Post || 
                 request.Method == HttpMethods.Put)
            {
                //Log the response body
                if (httpContext.Response.HasStarted)
                {
                    const string responseBody = "ResponseBody";

                    if (requestTelemetry.Properties.ContainsKey(responseBody))
                    {
                        return;
                    }

                    try
                    {
                        var stream = new StreamReader(response.Body);
                        var body = stream.ReadToEnd();                            
                        response.Body.Position = 0;
                        requestTelemetry.Properties.Add(responseBody, body);
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                }
            }
        }
    }
}  

----------------------------------UPDATE--------------------------------

Here is my full code to log Request and Response Body. Request body is logged properly (with request.EnableRewind();), However, response section is throwing exception on stream reader Stream was not readable.

public class RequestBodyAndResponseBodyInitializer : ITelemetryInitializer
{
    readonly IHttpContextAccessor httpContextAccessor;

    public RequestBodyAndResponseBodyInitializer(IHttpContextAccessor httpContextAccessor)
    {
        this.httpContextAccessor = httpContextAccessor;
    }

    public void Initialize(ITelemetry telemetry)
    {
        if (telemetry is RequestTelemetry requestTelemetry)
        {
            HttpContext httpContext = httpContextAccessor.HttpContext;
            HttpRequest request = httpContext.Request;
            HttpResponse response = httpContext.Response;

            if (request.Method == HttpMethods.Post ||
                 request.Method == HttpMethods.Put)
            {
                //1- Log the request body
                if (request.Body.CanRead)
                {
                    const string requestBody = "RequestBody";
                    if (requestTelemetry.Properties.ContainsKey(requestBody))
                    {
                        return;
                    }

                    //Allows re-usage of the stream
                    request.EnableRewind();

                    var stream = new StreamReader(request.Body);
                    var body = stream.ReadToEnd();

                    //Reset the stream so data is not lost
                    request.Body.Position = 0;
                    requestTelemetry.Properties.Add(requestBody, body);
                }
                //2- Log the response body
                else if (httpContext.Response.HasStarted)
                {
                    //Allows re-usage of the stream
                    //request.EnableRewind();

                    const string responseBody = "ResponseBody";

                    if (requestTelemetry.Properties.ContainsKey(responseBody))
                    {
                        return;
                    }

                    try
                    {
                        //var stream = new StreamReader(response.Body);
                        //var body = stream.ReadToEnd();                            
                        //response.Body.Position = 0;
                        //requestTelemetry.Properties.Add(responseBody, body);

                        using (var memoryStream = new MemoryStream())
                        {
                            var stream = response.Body;
                            response.Body = memoryStream;

                            await next(context);

                            var body = new StreamReader(memoryStream).ReadToEnd();
                            //logger?.LogDebug($"Response: {responseBody}");

                            response.Body = stream;
                        }


                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                }
                else { }
            }
        }
    }
}
Muhammad Umar
  • 1,357
  • 13
  • 14

3 Answers3

1

You have to implement ITelemetryInitializer. Inject the IHttpContextAccessor to the class and read the response stream within the Initialize method. Ensure the passed ITelemetry object is from type RequestTelemetry and that the HttpRequest was either a Post or Put. Then you can read the response using the IHttpContext.HttpContext.Response.Body property and log it using Application Insight.

Finally, register your class within the ConfigureService method in your Startup.cs

Martin Brandl
  • 56,134
  • 13
  • 133
  • 172
  • Yes, I did try that, but getting error while creating stream on response body :(. I followed the link https://stackoverflow.com/questions/42686363/view-post-request-body-in-application-insights – Muhammad Umar Aug 03 '18 at 11:27
  • I have modified the question with the exception info and code snippet. – Muhammad Umar Aug 03 '18 at 11:41
  • Add `context.Request.EnableRewind()` (Yes, on the Request object, not on the Response). Explanation: https://stackoverflow.com/questions/43403941/how-to-read-asp-net-core-response-body/43404745 – Martin Brandl Aug 03 '18 at 12:59
  • I have pasted my full code in UPDATED section of question that also makes use of Request.EnableRewind() but still nothing fruitful is achieved. – Muhammad Umar Aug 03 '18 at 14:14
1

I've solved it by adding a middleware that saves the request body as a feature in the HttpContext. It's important that the middleware is registtered before app.UseMvc() since the Mvc framework disposes the stream.

Startup.cs

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.Use(async (httpContext, next) =>
        {
            var request = httpContext.Request;

            if (request.Method == HttpMethods.Post || request.Method == HttpMethods.Put && request.Body.CanRead)
            {
                //Allows re-usage of the stream
                request.EnableBuffering();
                request.Body.Seek(0, SeekOrigin.Begin);

                using (var stream = new StreamReader(request.Body, Encoding.UTF8, true, 1024, true))
                {
                    var body = await stream.ReadToEndAsync();
                    //Reset the stream so data is not lost
                    request.Body.Position = 0;
                    var bodyFeature = new RequestBodyFeature(body); // RequestBodyFeature is a simple POCO
                    httpContext.Features.Set(bodyFeature);
                }

                request.Body.Seek(0, SeekOrigin.Begin);
            }

            await next.Invoke();
        });

        app.UseMvc();
}

I'm then getting the body like this when I'm logging to ApplicationInsights from my ExceptionFilter:

context.HttpContext?.Features.Get<RequestBodyFeature>()?.Body

I came to that conclusion after reading this issue on the ApplicationInsights github page and this issue on the AspNetCore github page.

Johan Gov
  • 1,262
  • 1
  • 13
  • 26
-1

I solved the above problem, I explained the solution in the following blog. Basically it involves storing the response in request message using message handler and retrieving the same in the class that implements ITelemetryInitialize.

Here is the blog that explains in details.

https://thirum.wordpress.com/2019/08/19/logging-the-http-response-body-in-application-insights/

Thiru
  • 407
  • 5
  • 7