bugsnag / bugsnag-dotnet

.NET notifier for BugSnag. Error monitoring and error reporting for .NET.
https://docs.bugsnag.com/platforms/dotnet/
MIT License
60 stars 29 forks source link

No request data in the report #129

Open KKSzymanowski opened 4 years ago

KKSzymanowski commented 4 years ago

Description

Request data, such as in a POST request, does not appear in the report in Bugsnag.

Steps to reproduce

  1. Throw some exception in a controller, for example:
    
    using SubiektOptyk.Requests;
    using SubiektOptyk.Responses;
    using System;

namespace SubiektOptyk.Controllers { public class AuthController : ApiController { public LoginResponse Post(LoginRequest loginRequest) { throw new Exception("test"); } } }


2. Make a request to the controller with some data
![image](https://user-images.githubusercontent.com/13980973/94940271-92a79980-04d3-11eb-96ed-f631dd0ba857.png)
3. See that the request data does not appear in the report.
![image](https://user-images.githubusercontent.com/13980973/94940394-bf5bb100-04d3-11eb-8883-f51e0a0f7d12.png)

How do I make Bugsnag display the request information like with the [Laravel (PHP) plugin](https://github.com/bugsnag/bugsnag-laravel)?

### Environment
* Bugsnag version: 2.2.1
* .NET framework version: 4.7.2
KKSzymanowski commented 4 years ago

I have managed to include the request data by creating a base class for all my API controllers and overriding the Initialize method in a following way:

protected override void Initialize(HttpControllerContext controllerContext)
{
    base.Initialize(controllerContext);

    Request.Bugsnag().BeforeNotify(report => {
        Stream result = Request.Content.ReadAsStreamAsync().Result;
        result.Seek(0, SeekOrigin.Begin);

        StreamReader streamReader = new StreamReader(result);
        string content = streamReader.ReadToEnd();

        report.Event.Metadata.Add("Request data", content);
    });
}

It seems like a hack, though. Is there a better way to do this?

mattdyoung commented 4 years ago

Hi @KKSzymanowski

Bugsnag doesn't currently capture the request body on .NET, but this seems a reasonable feature request to align with how we capture this on other platforms.

Adding the request body in a BeforeNotify callback is what we'd recommend for now.

KKSzymanowski commented 4 years ago

Thanks for your response. The issue is resolved for me but if you'd like to keep the conversation going for the new feature discussion here feel free to leave this open.

Daniel15 commented 3 years ago

In ASP.NET Core, you can avoid having to modify your controllers by using custom middleware to add the POST data to the Bugsnag report. I just tried this and it works great:

using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Bugsnag;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;

namespace MyExampleApp.Web.Middleware
{
    public class AddPostDataToBugsnagReportMiddleware
    {
        private readonly RequestDelegate _next;

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

        public Task Invoke(HttpContext context, IClient bugsnag)
        {
            context.Request.EnableBuffering();
            bugsnag.BeforeNotify(report =>
            {
                if (context.Request.HasFormContentType)
                {
                    // x-www-form-urlencoded data; log as key-value pairs
                    var form = context.Request.Form;
                    if (form.Count > 0)
                    {
                        report.Event.Request.Add(
                            "post", 
                            form.ToDictionary(x => x.Key, x => x.Value)
                        );
                    }
                }
                else
                {
                    // Some other type of data; log as raw POST body
                    using var reader = new StreamReader(
                        context.Request.Body,
                        Encoding.UTF8,
                        leaveOpen: true
                    );
                    // NOTE: This is a synchronous read, however Bugsnag doesn't have an async BeforeNotify
                    var body = reader.ReadToEndAsync().Result;
                    if (body.Length > 0)
                    {
                        report.Event.Metadata.Add("post", body);
                    }
                    context.Request.Body.Position = 0;
                }
            });
            return _next(context);
        }
    }

    public static class AddPostDataToBugsnagReportMiddlewareExtensions
    {
        public static IApplicationBuilder UseAddPostDataToBugsnagReportMiddleware(
            this IApplicationBuilder builder
        )
        {
            return builder.UseMiddleware<AddPostDataToBugsnagReportMiddleware>();
        }
    }
}

Then call app.UseAddPostDataToBugsnagReportMiddleware() very early in the pipeline in Configure (I added it as the first middleware).

This does not do any redaction (unless Bugsnag does that as a separate step?) however my app is public with no access tokens so it's not an issue for me.

luke-belton commented 3 years ago

Hey @Daniel15 - thanks for sharing your approach. Bugsnag supports redaction of sensitive data, but only based on metadata keys. Because your approach adds the post body as a string, Bugsnag will not redact any sensitive data contained in this body. There's more info about how to redact keys in our docs: https://docs.bugsnag.com/platforms/dotnet/asp-net-core/configuration-options/#metadata-filters.

For future travellers - a workaround would be to consider deserializing your post body to e.g. Dictionary<string, object> before adding it to the Bugsnag event metadata if redacting sensitive data is of concern.

Daniel15 commented 3 years ago

I do deserialize it to a dictionary in the code above, but only if it's form data. Other formats like JSON are just sent raw, but support could be added.

The other thing to watch out for is if the POST body is very large (eg. for file uploads) in my case the site only sends POST requests as form data, so it's fine.