MinimalApiPlayground icon indicating copy to clipboard operation
MinimalApiPlayground copied to clipboard

ProblemDetailsServiceEndpointFilter stripping HttpValidationProblemDetails

Open kingdamo opened this issue 2 years ago • 2 comments

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
                });
            }
        }
    }
}

kingdamo avatar Dec 13 '22 06:12 kingdamo

Seems this is caused by an issue in ASP.NET Core itself: https://github.com/dotnet/aspnetcore/issues/45680

DamianEdwards avatar Dec 19 '22 19:12 DamianEdwards

Seems this is caused by an issue in ASP.NET Core itself: [dotnet/aspnetcore#45680]

Thanks Damian, I'll follow on with that issue.

kingdamo avatar Dec 28 '22 05:12 kingdamo