Marius Schulz
Marius Schulz
Front End Engineer

Generating Route URLs in ASP.NET Core MVC

When I work on a web application, I put a lot of thought into its URLs. I want every page to have a clean and readable URL that can be deciphered even by a non-technical user. I don't want /Users/Profile/42. I want /users/42. Both the uppercase letters and the MVC action name bother me.

A pretty URL

For this reason, I don't rely on the standard {controller}/{action}/{id?} route template. Instead, I define a custom route template for every action that's visible to the user. Doing it this way is more work, admittedly, but it gives me ultimate control over route readability.

Here's an example of a route definition using the centralized routing strategy within the Configure method of the Startup class:

public void Configure(IApplicationBuilder app)
{
    // ...

    app.UseMvc(routes =>
    {
        routes.MapRoute("UserProfile", "users/{id}",
            new { controller = "Users", action = "Profile" });
    });
}

Another option is to use attribute routing, as illustrated here:

public class UsersController : Controller
{
    [Route("users/{id}", Name = "UserProfile")]
    public ActionResult Profile(int id)
    {
        // ...

        return View();
    }
}

Note that I specified the Name property of the route. I can now generate a URL to that action using the UrlHelper.RouteUrl method:

<a href="@Url.RouteUrl("UserProfile", new { id = 42 })">User Profile</a>

I like the RouteUrl method better than its Action sibling, which would require me to pass in "Users" and "Profile" for the controller and action names. However, I still don't like the string literal "UserProfile" because I'd have to remember all route names or look them up every time.

Because of that, I like to list all my route names in constant fields of a static class. This approach works nicely with the nameof operator introduced in C# 6:

public static class RouteNames
{
    // ...
    public const string UserProfile = nameof(UserProfile);
    // ...
}

Here's the updated URL generation code in my Razor view. Now, when I type RouteNames., smart tooling like IntelliSense can suggest all available route names to me:

<a href="@Url.RouteUrl(RouteNames.UserProfile, new { id = 42 })">User Profile</a>

I can also replace the string literal for the route name within the route definition by a reference to RouteNames.UserProfile:

routes.MapRoute(RouteNames.UserProfile, "users/{id}",
    new { controller = "Users", action = "Profile" });

Similarly, I can set the Name property of the [Route] attribute to RouteNames.UserProfile if I'm using attribute routing instead:

public class UsersController : Controller
{
    [Route("users/{id}", Name = RouteNames.UserProfile)]
    public ActionResult Profile(int id)
    {
        // ...

        return View();
    }
}

I've been using this approach for years, and it has worked very well for me so far. All route URLs look nice and clean. Also, I'm free to change my route templates without having to update any Razor views (as long as I keep the parameter names).