MinimalApiPlayground
MinimalApiPlayground copied to clipboard
ProblemDetailsServiceEndpointFilter stripping HttpValidationProblemDetails
Hi @DamianEdwards,
I'm trying to use the ProblemDetailsServiceEndpointFilter inside of this repository and it mostly works great.
When I run the API, calling /test it returns a response of:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"Value1": [
"Problem 1",
"Problem 2"
]
}
}
However if I use the /testfiltered if returns a response of:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400
}
While debugging I can see that it gets to the ProblemDetailsServiceAwareResult.ExecuteAsync
method as the Microsoft.AspNetCore.Http.HttpValidationProblemDetails
but then the extra validation properties are lost.
Even explicitly using that type as the Value type, it doesn't solve the problem.
Any help would be appreciated.
Regards, Damien.
Program.cs:
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerDocument();
builder.Services.AddProblemDetails(
);
var app = builder.Build();
app.UseExceptionHandler();
app.UseStatusCodePages();
if (app.Environment.IsDevelopment())
{
app.UseOpenApi();
app.UseSwaggerUi3();
}
app.MapGet("/test", () =>
{
var dict = new Dictionary<string, string[]>();
dict.Add("Value1", new[] { "Problem 1", "Problem 2" });
return Results.ValidationProblem(dict);
});
var general = app.MapGroup("").AddEndpointFilter(new ProblemDetailsServiceEndpointFilter());
general.MapGet("/testfiltered", () =>
{
var dict = new Dictionary<string, string[]>();
dict.Add("Value1", new[] { "Problem 1", "Problem 2" });
return Results.ValidationProblem(dict);
});
app.Run();
public class ProblemDetailsServiceEndpointFilter : IEndpointFilter
{
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
=> await next(context) switch
{
ProblemHttpResult problemHttpResult => new ProblemDetailsServiceAwareResult(problemHttpResult.StatusCode, problemHttpResult.ProblemDetails),
ProblemDetails problemDetails => new ProblemDetailsServiceAwareResult(null, problemDetails),
{ } result => result,
null => null
};
private class ProblemDetailsServiceAwareResult : IResult, IValueHttpResult, IValueHttpResult<ProblemDetails>
{
private readonly int? _statusCode;
public ProblemDetailsServiceAwareResult(int? statusCode, ProblemDetails problemDetails)
{
_statusCode = statusCode ?? problemDetails.Status;
Value = problemDetails;
}
public ProblemDetails Value { get; }
object? IValueHttpResult.Value => Value;
public async Task ExecuteAsync(HttpContext httpContext)
{
if (httpContext.RequestServices.GetService<IProblemDetailsService>() is IProblemDetailsService problemDetailsService)
{
if (_statusCode is { } statusCode)
{
httpContext.Response.StatusCode = statusCode;
}
await problemDetailsService.WriteAsync(new()
{
HttpContext = httpContext,
ProblemDetails = Value
});
}
}
}
}
Seems this is caused by an issue in ASP.NET Core itself: https://github.com/dotnet/aspnetcore/issues/45680
Seems this is caused by an issue in ASP.NET Core itself: [dotnet/aspnetcore#45680]
Thanks Damian, I'll follow on with that issue.