Routing Templates
So far, the routes we have used have been very basic - a static path, sometimes combined with a simple, variable parameter. However, the routing system in ASP.NET MVC has a LOT more to offer. You can in fact create very advanced URL routes, using route templates. We'll look into some of the possibilities in this article.
In a previous article, we used the {id} syntax to map a part of the URL to a parameter called "id" in the receiving controller method. This is one of the most basic routing templates you can use, but you are not limited to using just one variable/parameter in your routes. In fact, you can use as many as you want and you are free to combine them in any way you see fit. This could, for example, be used to create a URL with both an ID and a slug, as seen in many blog systems like Wordpress etc.
Here's an example of a routing template where we combine two parameters to create the classical [ID]/[URL_SLUG] URL:
[Route("blog/{entryId}/{slug}")]
public IActionResult Blog(int entryId, string slug)
{
return Content($"Blog entry with ID #{entryId} requested (URL Slug: {slug})");
}
You can now reach this action using a URL like this:
/blog/153/testing-asp-mvc-routes/
"153" will then be mapped to the entryId parameter, while "testing-asp-mvc-routes" will be mapped to the slug parameter and based on that, the following output will be generated by our action method:
Blog entry with ID #153 requested (URL Slug: testing-asp-mvc-routes)
Catch-all parameters
If you test the above route, you will notice that your slug can't contain forward slashes (/), like this one:
/blog/153/testing-the/routing-system/
The reason is probably obvious: The forward slash is normally used in URL's to separate folder names, and even though your route doesn't necessarily relate to a specific folder, the forward slash still has a special meaning. In this case, it would seem that you are trying to reach either two folders or two parameters with the values "testing-the" and "routing-system". This might not be what you want, though - perhaps in your system, it makes sense to allow forward slashes as part of the URL slug. For situations like this, ASP.NET MVC includes the so-called catch-all parameters.
A catch-all parameter is created by prefixing the name of the parameter with an asterisk (*) character, like this:
[Route("blog/{entryId}/{*slug}")]
public IActionResult Blog(int entryId, string slug)
{
return Content($"Blog entry with ID #{entryId} requested (URL Slug: {slug})");
}
Our URL from before will now allow for the forward slash in the slug parameter:
URL:
/blog/153/testing-the/routing-system/
Output:
Blog entry with ID #153 requested (URL Slug: testing-asp/mvc-routes)
Optional parameters
In the examples above, both the entryId and the slug parameters were required - if omitted in the URL, the route template would simply not match the requested URL and the result will be either a "404 Page not Found" or it would be caught by the default/fallback route. However, sometimes you want to make one or several paramters optional, meaning that they can be omitted without the route failing to match the URL request.
For instance, you may decide that the URL slug for your blog posts should be optional - after all, the ID is enough to identify the desired blog post and display it to the user, while the slug part is often included for SEO/readability reasons. Making the slug part of the URL optional is as simple as adding a question mark (?) to the parameter name:
[Route("blog/{entryId}/{slug?}")]
public IActionResult Blog(int entryId, string slug)
{
return Content($"Blog entry with ID #{entryId} requested (URL Slug: {slug})");
}
You can now call the action method with a URL like this one:
/blog/153/
If you do so, the slug parameter will be NULL. This is not a problem when using it for output the way we do, but if you tried using the value for other purposes, it could result in a NullReferenceException. To remedy this, you should either check for NULL before using it, or, if it makes more sense in your case, add a default string value to the parameter (empty or non-empty) like this:
[Route("blog/{entryId}/{slug?}")]
public IActionResult Blog(int entryId, string slug = "")
{
....
Reserved routing names
When creating your routes and specifying, for instance, parameter names, you should pay attention to the following words, which are reserved within the routing system and therefore shouldn't be used as names/parameter names:
- action
- area
- controller
- handler
- page
Summary
As you can see, URL routing is very flexible when using the template functionality illustrated in this article. However, sometimes a route can become TOO flexible, meaning that it might conflict with other routes in your website. For these situations, among others, you need to take more control of your routes - this can be achieved with routing constraints, which we'll discuss in the next article!