-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Description
Is there an existing issue for this?
- I have searched the existing issues
Is your feature request related to a problem? Please describe the problem.
I created a generic endpoint filter
public class EndpointRequestResponseLoggingFilter<TRequest, TResponse> : IEndpointFilterwhich has the purpose to log the request and response objects of an endpoint.
As a side note, I know that .NET 7 offers some similar functionality but it's not good enough for my needs (because it doesn't allow me to mark sensitive properties on the request/response object and filter them).
I made some extension methods to simplify the registration of the endpoint filter:
public static RouteHandlerBuilder AddRequestResponseLoggingFilter<TRequest, TResponse>(this RouteHandlerBuilder builder)
{
builder.AddEndpointFilter<EndpointRequestResponseLoggingFilter<TRequest, TResponse>>();
return builder;
}
public static RouteHandlerBuilder AddRequestLoggingFilter<TRequest>(this RouteHandlerBuilder builder)
{
builder.AddEndpointFilter<EndpointRequestResponseLoggingFilter<TRequest, EmptyHttpResult>>();
return builder;
}
public static RouteHandlerBuilder AddResponseLoggingFilter<TResponse>(this RouteHandlerBuilder builder)
{
builder.AddEndpointFilter<EndpointRequestResponseLoggingFilter<EmptyHttpRequest, TResponse>>();
return builder;
}and I use them to register the filter on some endpoints:
app.MapPost("/myEndpoint", (MyRequest myRequest) => new MyResponse(1, "Message"))
.AddRequestResponseLoggingFilter<MyRequest, MyResponse>();
app.MapPost("/myEndpointWithNoResponse", (MyRequest myRequest) => { })
.AddRequestLoggingFilter<MyRequest>();
app.MapPost("/myEndpointWithNoRequest", () => new MyResponse(1, "abc"))
.AddResponseLoggingFilter<MyResponse>();Inside the filter I have this logic:
public virtual async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
{
// Log request
if (context.Arguments.Count > 0)
{
var request = context.Arguments[0];
if (request is TRequest)
// Filter sensitive properties on the request and log the rest of the object
else if (request is null)
_logger.LogError("Request: null");
else
{
_logger.LogError("Expected request type '{providedRequestType}' but found type '{actualRequestType}'.", typeof(TRequest).FullName, request.GetType().FullName);
// Filter sensitive properties on the request and log the rest of the object
}
}
else if (typeof(TRequest) != typeof(EmptyHttpRequest))
_logger.LogError("Expected request type '{providedRequestType}' but no request object was received.", typeof(TRequest).FullName);
else
_logger.LogInformation("Request: No request");
var response = await next(context);
// Log response
if (response is TResponse)
// Filter sensitive properties on the response and log the rest of the object
else
{
if (response is null)
_logger.LogError("Response: null");
else
{
if (response.GetType().Name is nameof(EmptyHttpResult))
{
if (typeof(TResponse) != typeof(EmptyHttpResult))
_logger.LogError("Expected response type '{providedResponseType}' but no response was returned.", typeof(TResponse).FullName);
else
_logger.LogInformation("Response: No response");
}
else
{
_logger.LogError("Expected response type '{providedResponseType}' but found type '{actualResponseType}'.", typeof(TResponse).FullName, response.GetType().FullName);
// Filter sensitive properties on the response and log the rest of the object
}
}
}
return response;
}As you can see, I am also doing some validations and type checking to make let the developer know that the type he registered on the endpoint filter is not the same as the actual object type.
The EmptyHttpRequest is a record I created to represent the situation when an endpoint doesn't have a request object.
The EmptyHttpResult is what I saw that the framework returns on the response when the endpoint has no response (e.g.: "myEndpointWithNoResponse"). So when I have an endpoint with no response I register the filter like this builder.AddEndpointFilter<EndpointRequestResponseLoggingFilter<TRequest, EmptyHttpResult>>()
The problem though is that I have to do the comparison like this: if (response.GetType().Name is nameof(EmptyHttpResult)) because the public typed exposed by the framework is Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult while the type of the response object is Microsoft.AspNetCore.Http.RequestDelegateFactory+EmptyHttpResult.
Describe the solution you'd like
I would like the response object to have the type Microsoft.AspNetCore.Http.HttpResults.EmptyHttpResult so I can do the comparison in a much simpler way: if (response is EmptyHttpResult).
Additional context
I am using the dotnet version: 7.0.100