How do you enforce lowercase routing in ASP.NET Core?

37,706

Solution 1

For ASP.NET Core:

Add one of the following lines to the ConfigureServices method of the Startup class:

services.AddRouting(options => options.LowercaseUrls = true);

or

services.Configure<RouteOptions>(options => options.LowercaseUrls = true); 

Thanks to Skorunka for the answer as a comment. I thought it was worth promoting to an actual answer.

Solution 2

Update in ASP.NET Core Version >= 2.2

From ASP.NET Core 2.2, along with lowercase you can also make your route dashed using ConstraintMap which will make your route /Employee/EmployeeDetails/1 to /employee/employee-details/1 instead of /employee/employeedetails/1.

To do so, first create the SlugifyParameterTransformer class should be as follows:

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

For ASP.NET Core 2.2 MVC:

In the ConfigureServices method of the Startup class:

services.AddRouting(option =>
{
    option.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer);
});

And Route configuration should be as follows:

app.UseMvc(routes =>
{
     routes.MapRoute(
        name: "default",
        template: "{controller:slugify}/{action:slugify}/{id?}",
        defaults: new { controller = "Home", action = "Index" });
});

For ASP.NET Core 2.2 Web API:

In the ConfigureServices method of the Startup class:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options => 
    {
        options.Conventions.Add(new RouteTokenTransformerConvention(new SlugifyParameterTransformer()));
    }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

For ASP.NET Core >=3.0 MVC:

In the ConfigureServices method of the Startup class:

services.AddRouting(option =>
{
    option.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer);
});

And Route configuration should be as follows:

app.UseEndpoints(endpoints =>
{
      endpoints.MapAreaControllerRoute(
          name: "AdminAreaRoute",
          areaName: "Admin",
          pattern: "admin/{controller:slugify=Dashboard}/{action:slugify=Index}/{id:slugify?}");

      endpoints.MapControllerRoute(
          name: "default",
          pattern: "{controller:slugify}/{action:slugify}/{id:slugify?}",
          defaults: new { controller = "Home", action = "Index" });
});

For ASP.NET Core >=3.0 Web API:

In the ConfigureServices method of the Startup class:

services.AddControllers(options => 
{
    options.Conventions.Add(new RouteTokenTransformerConvention(new SlugifyParameterTransformer()));
});

For ASP.NET Core >=3.0 Razor Pages:

In the ConfigureServices method of the Startup class:

services.AddRazorPages(options => 
{
    options.Conventions.Add(new PageRouteTransformerConvention(new SlugifyParameterTransformer()));
})

This is will make /Employee/EmployeeDetails/1 route to /employee/employee-details/1

Solution 3

As other answers indicate, adding:

services.Configure<RouteOptions>(options => options.LowercaseUrls = true);

before

services.AddMvc(...)

works great, but I also want to add that if you use Identity, you will also need:

services.AddIdentity<IdentityUser, IdentityRole>(options =>
{
    var appCookie = options.Cookies.ApplicationCookie;
    appCookie.LoginPath = appCookie.LoginPath.ToString().ToLowerInvariant();
    appCookie.LogoutPath = appCookie.LogoutPath.ToString().ToLowerInvariant();
    appCookie.ReturnUrlParameter = appCookie.ReturnUrlParameter.ToString().ToLowerInvariant();
});

And obviously, replace both IdentityUser, and IdentityRole with your own classes if required.

I just tested this with .NET Core SDK 1.0.4 and the 1.0.5 runtime.

Solution 4

Found the solution.

In the assembly: Microsoft.AspNet.Routing, and the Microsoft.Extensions.DependencyInjection namespace, you can do this in your ConfigureServices(IServiceCollection services) method:

services.ConfigureRouting(setupAction =>
{
    setupAction.LowercaseUrls = true;
});

Solution 5

It is worth noting that setting:

services.Configure<RouteOptions>(options => options.LowercaseUrls = true);

does not affect query strings.

To ensure that query strings are also lowercase, set the options.LowercaseQueryStrings to true:

services.Configure<RouteOptions>(options => 
{ 
    options.LowercaseUrls = true; 
    options.LowercaseQueryStrings = true;
});

However, setting this property to true is only relevant if options.LowercaseUrls is also true. options.LowercaseQueryStrings property is ignored if options.LowercaseUrls is false.

Share:
37,706
mariocatch
Author by

mariocatch

Updated on August 10, 2021

Comments

  • mariocatch
    mariocatch almost 3 years

    In ASP.NET 4 this was as easy as routes.LowercaseUrls = true; in the RegisterRoutes handler for the app.

    I cannot find an equivalent in ASP.NET Core for achieving this. I'd think it would be here:

    app.UseMvc(configureRoutes =>
    {
        configureRoutes.MapRoute("Default", "{controller=App}/{action=Index}/{id?}");
    });
    

    But nothing in configureRoutes looks to allow it... unless there's an extension method somewhere that I can't find in the docs perhaps?

  • Skorunka František
    Skorunka František almost 8 years
    For ASP NET MVC CORE: services.AddRouting(options => { options.LowercaseUrls = true; });
  • Skorunka František
    Skorunka František almost 8 years
    Microsoft.Extensions.DependencyInjection in Microsoft.AspNetCore.Routing.dll
  • Yves Schelpe
    Yves Schelpe over 7 years
    This was true before RTM, now you should use .AddRouting in stead of .ConfigureRouting
  • Nick Albrecht
    Nick Albrecht over 7 years
    It's worth noting you should put this BEFORE you actually call AddMvc() in your Startup.ConfigureServices() method. AddRouting() which is also called by AddMvc()uses the Try variants of the methods for adding dependencies to your service collection. So when it sees that the routing dependencies have already been added, it will skip that parts of AddMvc() setup logic.
  • mariocatch
    mariocatch about 7 years
    Switching this to correct answer since mine was during the transition of asp 4 to core.
  • Sam Alekseev
    Sam Alekseev over 5 years
    I tried this code and official Microsoft code too, but "slugify" parameter transformer has no effect. It is just ignored by routing system (so URLs not replaced to dashed ones). To check myself i put logger into TransformOutbound() method, but no calls from there.
  • TanvirArjel
    TanvirArjel over 5 years
    Okay! Let me check please!
  • TanvirArjel
    TanvirArjel over 5 years
    @user3172616 I have check it right now! its working as expected! like generating route as employee-details. Would you show me your configuration please?
  • TanvirArjel
    TanvirArjel over 5 years
    @user3172616 are you using attribute routing on the route that you tested?
  • Sam Alekseev
    Sam Alekseev over 5 years
    I use standard routing approach (not attribute routing) on brand new core v2.2 solution in Visual Studio. There are two screenshots (c# code and .cshtml code). I tried to generate links in different ways with no effect. imgur.com/a/L8dCF6c
  • TanvirArjel
    TanvirArjel over 5 years
    Found nothing wrong in your code! Would give a try removing SlugifyParameterTransformer class from startup class to anywhere?
  • Sam Alekseev
    Sam Alekseev over 5 years
    I moved SlugifyParameterTransformer to separate *.cs file with no success. Generated urls are still the same like "/home/thisisthetest". Also i tried to throw random exception from there, it has no effect. But if i comment code line where i register this Transformer then app crashes because it uses unknown "slugify" placeholder in the route as expected.
  • TanvirArjel
    TanvirArjel over 5 years
    Sorry to hear that! Don't know what wrong happening in case of you while its working fine in my application.
  • TanvirArjel
    TanvirArjel over 5 years
  • T-moty
    T-moty about 4 years
    Configure<RouteOptions>() is the best answer imho: tiny and straight to the point (tested on mvc core 3.1)
  • Thomas Levesque
    Thomas Levesque over 3 years
    @NickAlbrecht it doesn't seem to make a difference if it's called before or after (as of ASP.NET Core 5.0, at least). AddRouting() will be called twice anyway, so it doesn't matter in which order.
  • Nick Albrecht
    Nick Albrecht over 3 years
    I believe that was done around .NET Core 3.x. They changed it so that routing was a stand alone feature instead of bundled with MVC. I don't believe routing is called from the AddMvc (or AddControllersWithViews if you don't need RazorPages) anymore. So the order only really matters if you're using AspNetCore 2. (Don't recall if this was an option in 1.x). However they did split the lowercase behavior into two settings, so if you want fully lower case addresses, you need to set both LowercaseUrls and LowercaseQueryStrings to true.
  • Yaseer Arafat
    Yaseer Arafat about 3 years
    It's for asp.net mvc 5
  • carlin.scott
    carlin.scott over 2 years
    Can you clarify that the 2nd option is for Razor Pages and first for MVC?