Open KKSzymanowski opened 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?
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.
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.
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.
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.
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.
Description
Request data, such as in a POST request, does not appear in the report in Bugsnag.
Steps to reproduce
namespace SubiektOptyk.Controllers { public class AuthController : ApiController { public LoginResponse Post(LoginRequest loginRequest) { throw new Exception("test"); } } }