jezzsantos / saastack

A comprehensive codebase template for starting your real-world, fully featured SaaS web products. On the .NET platform
The Unlicense
15 stars 5 forks source link

[AsParameters] on a RequestDelegate does not honor the [JsonPropertyNameAttribute] applied to the request object #31

Open jezzsantos opened 3 months ago

jezzsantos commented 3 months ago

We can define a request DTO like this:

[Route("/avatar/{Hash}", OperationMethod.Get)]
public class GravatarGetImageRequest : IWebRequestVoid
{
    [JsonPropertyName("d")] public string? Default { get; set; }

    public required string Hash { get; set; }

    [JsonPropertyName("s")] public int? Width { get; set; }
}

We can see that we intend that the Default property be rendered in the request JSON as a field with name "d". When this request is sent, because it is a GET request, the value of Default will be converted to a query like this: /avatar/ahash?d=adefault

This is all well and good.

Now, in our web host, the handler is defined like this:

var stubgravatarapiGroup = app.MapGroup("/gravatar")
                .WithGroupName("StubGravatarApi")
                .RequireCors("__DefaultCorsPolicy")
                .AddEndpointFilter<ApiUsageFilter>()
                .AddEndpointFilter<RequestCorrelationFilter>()
                .AddEndpointFilter<ContentNegotiationFilter>();
            stubgravatarapiGroup.MapGet("/avatar/{Hash}",
                async (IMediator mediator, [Microsoft.AspNetCore.Http.AsParameters] GravatarGetImageRequest request) =>
                     await mediator.Send(request, CancellationToken.None));

Note the use of [AsParameters] on the request DTO object.

When our Host receives the request: /avatar/ahash?d=adefault we are expecting the Default property of the GravatarGetImageRequest request DTO to be populated by the value of the d parameter in the query, because of the existence of the [JsonPropertyName] attribute on that property.

But this is not the case. Instead the `Default' parameter is unpopulated once the request is handled.

In fact the ASPNET runtime will throw an exception if the Default property is declared as required instead of nullable, since the value is not populated by the request pipeline.

So there is something in the ASPNET pipeline that is ignoring the [JsonPropertyName] attribute