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.
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).