5

ASP.NET uses PascalCase URL components by default, but I'd like to use kebab-case instead. I have seen various articles and SO answers achieve this elegantly in ASP.NET Core via RouteTokenTransformerConvention (example code below). I like this strategy because it 1) ensures kebab-case routes are mapped to the correct controller and action with the correct route parameters without repetitive manual work and 2) allows you to generate matching kebab-case links.

However, it does not touch URL queries. Is there a way to get the above benefits but also include query keys in the transformation? For example:

  • I would want a URL like: some-route?product-category=some+product+category ...
  • To set the ProductCategory property of the bound model to "some product category"
    • (without me having to decorate ProductCategory with [FromQuery(Name="product-category")] or use this slightly better idea or use some other manual solution)
  • The query value should not be transformed - just the key.

Here's my code taken from the above links as a starting point:

Hyphenator.cs

using Microsoft.AspNetCore.Routing;
using System.Text.RegularExpressions;

public class Hyphenator: IOutboundParameterTransformer
{
    public string TransformOutbound(object value)
    {
        if (value == null) 
        { 
            return null; 
        }
        return Regex.Replace(value.ToString(), "([a-z])([A-Z])", "$1-$2").ToLower();
    }
}

Startup.cs

services.AddControllersWithViews(options =>
{
    options.Conventions.Add(new RouteTokenTransformerConvention(new Hyphenator()));
});

Edit for clarification:

I am hoping for a solution that does the transformation in the outbound direction (like the example code above) so that aside from model binding working, I could also pass regular PascalCase property names to code that generates URLs (e.g. anchor tag helpers and methods like RedirectToRoute()) and have the query keys in those URLs end up being kebab-case in the user's browser. Is this possible?

MarredCheese
  • 17,541
  • 8
  • 92
  • 91

1 Answers1

2

I would assume there are some efficiencies that could be made to the string manipulation code. This is a proof of concept. You could create a url rewriter rule to manipulate the query strings coming in.

public class MyRule : IRule
{
    public void ApplyRule(RewriteContext context)
    {
        Dictionary<string, StringValues> newQueryCollection = context.HttpContext.Request.Query.ToDictionary(
            kv => ToPascalCase(kv.Key),
            kv => kv.Value
        );

        context.HttpContext.Request.Query = new QueryCollection(newQueryCollection);
    }

    private static string ToPascalCase(string kebabCase)
    {
        return string.Join(string.Empty, kebabCase.Split('-').Select(str => str.Length > 0 ? char.ToUpper(str[0]) + str.Substring(1) : str));
    }
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    var options = new RewriteOptions().Add(new MyRule());
    app.UseRewriter(options);
    app.UseRouting();

    /*
        register other app builder extensions after this point
    */
}

If the rewriting takes place early in the pipeline, before the mvc middleware is run, then the updated keys for the query string parameters are what should be used to bind the model.

peinearydevelopment
  • 11,042
  • 5
  • 48
  • 76
  • Thanks for this! I may use it but will hold off accepting to see if I get something closer to what I was looking for. I added a bit of clarification to the question. – MarredCheese Jun 30 '20 at 21:23