Skip to content

DefaultProblemDetailsWriter does not serialize HttpValidationProblemDetails.Errors #45680

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
1 task done
DamianEdwards opened this issue Dec 19, 2022 · 0 comments · Fixed by #45906
Closed
1 task done
Assignees
Labels
bug This issue describes a behavior which is not expected - a bug. old-area-web-frameworks-do-not-use *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels
Milestone

Comments

@DamianEdwards
Copy link
Member

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

Setting ProblemDetailsContext.ProblemDetails to an instance of HttpValidationProblemDetails and subsequently passing that context instance to ProblemDetailsService.WriteAsync does not write out the error details contained on HttpValidationProblemDetails.Errors.

This is possibly caused by the fact that DefaultProblemDetailsWriter uses an internal JsonSerializerContext if no extensions are added to the problem details instance, and that JSON context is only decorated to support the ProblemDetails type.

Expected Behavior

Using ProblemDetailsService to write out an HttpValidationProblemDetails should include the errors in the resultant JSON content.

Steps To Reproduce

Create an endpoint filter that will use the IProblemDetailsService to write out the JSON content if an endpoint returns ProblemHttpResult or ProblemDetails directly:

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

Add two endpoints that return Results.ValidationProblem, one with the filter and one without:

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

Make requests to both endpoints and observe that the filtered endpoint doesn't include the error details.

Exceptions (if any)

No response

.NET Version

7.0.200-preview.22605.5

Anything else?

No response

@DamianEdwards DamianEdwards added bug This issue describes a behavior which is not expected - a bug. old-area-web-frameworks-do-not-use *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels labels Dec 19, 2022
@brunolins16 brunolins16 self-assigned this Dec 20, 2022
@halter73 halter73 added this to the 8.0-preview1 milestone Jan 5, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Feb 12, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug This issue describes a behavior which is not expected - a bug. old-area-web-frameworks-do-not-use *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants
@halter73 @DamianEdwards @brunolins16 and others