How to use enums in ASP.NET Core routes

This post will cover two things:

  1. How to use an enum in ASP.NET Core routes with the string value of the enum rather than the integer value
  2. How to have the Swagger UI show a dropdown of the enum values rather than needing to type in the string value

Thanks to Nick Heath and Thomas de Wulf for their very helpful blog posts on this topic on enum route constraints and user friendly enums in Swagger. This blog post is mostly a combination of those two posts, on top of me making the code (in my opinion) a bit cleaner and adding a working example.

I recently had a use case where I wanted to use an enum in my route URLs, such that the enum’s name would be displayed in the route. However, by default, ASP.NET Core uses the enum’s integer value, which doesn’t make for a very user-friendly route.

I also wanted to show a dropdown in the Swagger UI of the values for the enum, thereby constraining the call to only valid route values, and also making it easier to use through the Swagger UI. Although this is a simplified version made for this post, the end result looks something like this:

Screenshot of the Swagger UI showing the dropdown populated with the enum values

Let’s get to it. Here are the steps:

Making ASP.NET Core handle your enum in routes

  1. Create an IRouteConstraint
  2. Register the route constraint
  3. Use the route constraint in your routes

Creating the Swagger dropdown for the enum

  1. Create an EnumSchemaFilter
  2. Add the filter to Swagger generator
  3. Convert the enum to its string value in the route using a JsonStringEnumConverter

 

1. Create an IRouteConstraint

using EnumRoutes.Controllers;

namespace EnumRoutes;

public class RouteConstraint : IRouteConstraint
{
    public bool Match(HttpContext? httpContext, IRouter? route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
    {
        var matchingValue = values[routeKey]?.ToString();
        return Enum.TryParse(matchingValue, true, out TcgPlayerCategory _);
    }
}

 

2. Register the route constraint

builder.Services.Configure<RouteOptions>(options =>
{
    options.ConstraintMap.Add("tcgPlayerCategoryEnum", typeof(RouteConstraint));
});

Note: if you’re using a Startup.cs file rather than just a Program.cs, this will go in the ConfigureServices method.

The value you choose for the token is a bit of a magic string (then again, so are the built-in ones), so treat that how you will.

3. Use the route constraint in your routes

[HttpGet("{category:tcgPlayerCategoryEnum}")]
public ActionResult GetProducts([FromRoute] TcgPlayerCategory category)
{
    ...
}

 

Your enum should now work with your routes. If you don’t care about the Swagger dropdown, you’re done here!

 

4. Create an EnumSchemaFilter

using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;

namespace EnumRoutes;

public class EnumSchemaFilter : ISchemaFilter
{
    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        if (!context.Type.IsEnum)
        {
            return;
        }

        schema.Enum.Clear();

        foreach(var name in Enum.GetNames(context.Type))
        {
            schema.Enum.Add(new OpenApiString(name));
        }
    }
}

 

5. Add the filter to the Swagger generator

builder.Services.AddSwaggerGen(options =>
{
    options.SchemaFilter<EnumSchemaFilter>();
});

Note: if you’re using a Startup.cs file rather than just a Program.cs, this will go in the ConfigureServices method.

6. Convert the enum to its string value in the route using a JsonStringEnumConverter

builder.Services.AddControllers().AddJsonOptions(options =>
{
    options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});

 

A quick word of warning

When choosing to do this, ask yourself if it would not be simpler to just create multiple endpoints.

In my case, I originally had multiple controllers that had the same endpoints with the same logic, which was ultimately due to the TcgPlayer API routes. The enum allowed me to keep everything in one controller to share logic and prevent me from entering in invalid strings for which game I was querying for.

So, while adding the enum doesn’t require a lot of code, it’s nice to be sure that using an enum isn’t a code smell for your specific situation.

Github Example

You can find a full working example of this at the following Github repository: https://github.com/danielwarddev/EnumRoutes

 
 

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top