I'm using .NET Core 7, Minimal API and I would like to log POST request body.
I use following code:
var endpoints = app.MapGroup("api/v1")
.AddEndpointFilter<RequestResponseLogFilter>();
endpoints.MapPost("/endpoint", async ([FromServices] ServiceA serviceA, [FromServices] ServiceB serviceB, [FromBody] MyRequest request) =>
await MyEndpoint.DoSomething(serviceA, serviceB, request));
Attribute that I use to distinguish that object is request body:
public class RequestBodyAttribute : Attribute
{
}
[RequestBody]
public class MyRequest
{
}
Logging filter:
public class RequestResponseLogFilter : IEndpointFilter
{
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
{
var httpContext = context.HttpContext;
var logger = httpContext.RequestServices.GetService<ILogger>()!;
object? requestBody = context.Arguments.FirstOrDefault(a => a?.GetType().GetCustomAttribute<RequestBodyAttribute>() != null);
logger.Information("{@Request}", LogHelper.PrepareRequest(httpContext, requestBody));
var stopwatch = Stopwatch.StartNew();
var result = await next(context);
stopwatch.Stop();
logger.Information("{@Response}", LogHelper.PrepareResponse(httpContext, result, stopwatch.Elapsed));
return result;
}
I do not like approach that I need to put [RequestBody] attribute on each class that I use as POST body - this could lead to errors, I can forget to put attribute.
Another approach would be to use attribute position (for example last attribute is body), but this also could lead to errors.
I think that the better way would be to check for attribute [FromBody] (attribute is not mandatory, so there also is place for error), but I do not have this info in my filter.
What would be the best way to distinguish between services and body in my filter (assuming that I use generic filter to process any DTO)?