Skip to content

Introduce a more ergonomic way of enabling case-insensitive enum parsing in Minimal API endpoints #48346

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

Open
aradalvand opened this issue May 20, 2023 · 6 comments
Labels
area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc Needs: Design This issue requires design work before implementating.
Milestone

Comments

@aradalvand
Copy link

aradalvand commented May 20, 2023

Basically the same as #45590.
Currently, the only way to make this work would be to create another type that acts as a wrapper around the enum, just to implement a TryParse method that does case-insensitive parsing for the underlying enum; as @captainsafia described in the aforementioned issue:

You might want to consider implementing your own TryParse implementation on a wrapper for your MyEnum type that is case-insensitive. See more info at https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/parameter-binding?view=aspnetcore-7.0#tryparse

But it goes without saying that this is incredibly ugly.

So, either:

  • When binding enum parameters in minimal API handlers, use the Enum.TryParse overload that does a case-insensitive comparison. This way the behavior will also be consistent with that of MVC controllers. Or provide a global opt-in option that lets the developer decide.
  • Provide a simpler way to customize parameter binding on an individual-parameter-basis for minimal API handlers without having to define a TryParse or BindAsync method on the type of the parameter. This is a direly needed feature in general; as you don't always have access to the type of a parameter in such a way that you can declare a static TryParse or BindAsync method on them (as is effectively the case for enums, or for types that come from third-party libraries), so it should be possible to customize binding for individual parameters in a granular fashion, as opposed to having to modify the types themselves.

The second option is of course preferable.

@ghost ghost added the old-area-web-frameworks-do-not-use *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels label May 20, 2023
@aradalvand aradalvand changed the title Introduce a more ergonomic way of enabling case-insensitive enum parsing in minimal API Introduce a more ergonomic way of enabling case-insensitive enum parsing in Minimal API endpoints May 21, 2023
@captainsafia captainsafia added area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc Needs: Design This issue requires design work before implementating. and removed old-area-web-frameworks-do-not-use *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels labels May 22, 2023
@captainsafia
Copy link
Member

@aradalvand Thanks for filing this issue!

I see there's been some conversation in the related issue around customizing parameter binding in minimal APIs. I think this particular aspects needs some design and, to be frank, some more details about the types of granular modifications that need to be made.

I'll park this under Needs Design for now given the requirement to flesh this out. I'd be curious to see if you have thoughts on how to tackle this separate from what has been proposed in #35489.

@captainsafia captainsafia added this to the Backlog milestone May 22, 2023
@ghost
Copy link

ghost commented May 22, 2023

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

@adamijak
Copy link

You can use this GitHub gist.

With example usage:

app.MapGet("/", ([FromQuery] EnumBinding<MyEnum> param) => 
{
   MyEnum enumValue = param;
});

@captainsafia
Copy link
Member

@adamijak Thanks for sharing the code sample with folks! This is a good reminder that I should post some updates on #35489 based on some recent things I learned about how we might take advantage of keyed services in our parameter binding layer.

@dotnet-policy-service dotnet-policy-service bot added the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Feb 6, 2024
@wtgodbe wtgodbe removed the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Feb 6, 2024
@dotnet-policy-service dotnet-policy-service bot added the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Feb 6, 2024
@wtgodbe wtgodbe removed the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Feb 13, 2024
@dotnet dotnet deleted a comment from dotnet-policy-service bot Feb 13, 2024
@dotnet dotnet deleted a comment from dotnet-policy-service bot Feb 13, 2024
@jrebagliatti
Copy link

To add to @adamijak's workaround for those that want a proper Swagger documentation, you can add an schema filter like this one:

    public class EnumSchemaFilter : ISchemaFilter
    {
        public void Apply(OpenApiSchema schema, SchemaFilterContext context)
        {
            var type = context.Type;

            if (type == null)
            {
                return;
            }

            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(EnumBinding<>))
            {
                var enumType = type.GetGenericArguments().FirstOrDefault(x => x.IsEnum);

                if (enumType == null)
                {
                    return;
                }

                var names = Enum.GetNames(enumType);

                if (names == null)
                {
                    return;
                }
                
                schema.Type = "string";
                schema.Enum = names.OfType<string>().Select(x => new OpenApiString(x)).ToList<IOpenApiAny>();
            }
        }
    }

@adamijak
Copy link

adamijak commented Jan 6, 2025

@jrebagliatti @captainsafia I updated the gist. Now it accepts boolean and DateTime types as-well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc Needs: Design This issue requires design work before implementating.
Projects
None yet
Development

No branches or pull requests

6 participants
@captainsafia @jrebagliatti @wtgodbe @adamijak @aradalvand and others