MinimalApis.Extensions icon indicating copy to clipboard operation
MinimalApis.Extensions copied to clipboard

Combining Validated with a Record Struct Leads to InvalidProgram at Runtime

Open ErikFeeley opened this issue 2 years ago • 0 comments

Steps to reproduce.

  • dotnet new webapi -minimal.
  • Add reference to minimalapis.extensions.
  • Replace Program.cs with the following.
using Microsoft.AspNetCore.Http.HttpResults;
using MinimalApis.Extensions.Binding;
using System.ComponentModel.DataAnnotations;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsMetadataProviderApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapPost(
    "/inventory-accounts",
    Results<Ok<PostInventoryAccountRequest>, ValidationProblem> (
            Validated<PostInventoryAccountRequest> req) =>
    {
        if (!req.IsValid)
        {
            return TypedResults.ValidationProblem(req.Errors);
        }

        return TypedResults.Ok(req.Value);
    });

app.Run();

public record struct PostInventoryAccountRequest
{
    [Required]
    public string Id { get; set; }
};

Finally run up the app and hit the POST method via swagger UI. The following exception will be raised.

fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
      An unhandled exception has occurred while executing the request.
      System.InvalidProgramException: Common Language Runtime detected an invalid program.
         at RouteHandler.Execute(PostInventoryAccountRequest req, HttpContext httpContext)
         at lambda_method4(Closure , Object , HttpContext , Object )
         at Microsoft.AspNetCore.Http.RequestDelegateFactory.<>c__DisplayClass46_3.<<HandleRequestBodyAndCompileRequestDelegate>b__2>d.MoveNext()
      --- End of stack trace from previous location ---
         at MinimalApis.Extensions.Binding.DefaultBinder`1.GetValueAsync(HttpContext httpContext, ParameterInfo parameter) in /_/src/MinimalApis.Extensions/Binding/DefaultBinderOfT.cs:line 32
         at MinimalApis.Extensions.Binding.Validated`1.BindAsync(HttpContext context, ParameterInfo parameter) in /_/src/MinimalApis.Extensions/Binding/ValidatedOfT.cs:line 102
         at Microsoft.AspNetCore.Http.ParameterBindingMethodCache.<ConvertValueTask>g__ConvertAwaited|18_0[T](ValueTask`1 typedValueTask)
         at Microsoft.AspNetCore.Http.RequestDelegateFactory.<>c__DisplayClass46_1.<<HandleRequestBodyAndCompileRequestDelegate>b__0>d.MoveNext()
      --- End of stack trace from previous location ---
         at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
         at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
         at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
         at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

I am not entirely sure what is going on here but I think it could possibly be related to a bug with records mentioned over here -> https://stackoverflow.com/questions/70904674/why-does-this-tiny-program-give-common-language-runtime-detected-an-invalid-pro

I also understand that record structs are probably not meant to be used in this way, but when I saw the exception I thought it was interesting and worth making a quick issue about. I tested this on 6.0.5 originally, and found that the same thing happens on preview 4 of dotnet 7 with the built in version of this library.

Finally I wanted to say thanks for creating this library! I have had a lot of fun playing around with minimal APIs and these extensions improve the experience. Im excited for the future direction and functionality coming!

ErikFeeley avatar May 12 '22 14:05 ErikFeeley