TOC
Routing:

The anatomy of an MVC Route

In this tutorial, we have already talked about how you can easily get up and running with basic routing support thanks to default routing. However, it's a great idea to understand just how routes are defined, because at one point, you will likely need to step outside the comfort of the default/catch-all route and create your own. With that in mind, let's talk a bit about how a route is defined and what it looks like.

The most important part of a routing rule is to translate a URL into a Controller method. For that, we need the URL path and the name of the Controller and the method. The system needs to be able to identify the rule (and you might also like a reminder about the purpose of the rule, if you have a lot of them), so we also need to give the rule a name.

With that in mind, let's try creating a very basic rule. As mentioned, rules can be defined in several ways and places, but we'll start out doing it in the Startup.cs file, inside the Configure() method:

app.UseMvc(routes =>
routes.MapRoute("ProductList", "Products/List/", new { controller = "Products", action = "List" })
);

The call to MapRoute() takes several parameters - in this case, we supply a name for the route (the first parameter), the URL template for the rule (the second parameter), as well as an object of "defaults" (the third parameter), which will direct the rule to the desired Controller and method on that Controller. So, with this in place, we can now call the URL http://localhost/Products/List, and the List() method on the ProductsController will be called.

The Route template

In the above example, the most interesting parameter is the second one. It defines the template of the Route and while our template was very simple, there are a lot more possibilities, allowing you to support very sophisticated and complex URL's. First of all, you can define the default controller and method/action directly in the template, like this:

routes.MapRoute("Default", "{controller=Home}/{action=Index}")

This is generally referred to as conventional routing, because it consists of at least two parts, where the first part refers to the controller method and the second part refers to the action/method. The third and optional part is usually an ID, allowing you to refer to a specific entity you would like to display, e.g. a specific product instead of a list of products and so on. You can define this optional ID parameter in the route as well, using a special syntax. Let's try a more complex example of a route, where we use several of the techniques just described:

app.UseMvc(routes =>
routes.MapRoute("Products", "Products/{action=Index}/{id?}", new { controller = "Products" })
);

The template now matches URL's which starts with the word "products". If nothing else is specified, it will default to the Index action/method. On the other hand, if an action is specified in the second part of the URL, it will be used. At the end, we have an optional ID parameter - the fact that it's optional is noted with the use of a question mark. We now support the following URL's:

  • /Products/
  • /Products/List/
  • /Products/Details/32/

The Controller to handle all these scenarios could look like this:

public class ProductsController : Controller
{
public IActionResult Index()
{
    return Content("Product overview");
}

public IActionResult List()
{
    return Content("Product list");
}

public IActionResult Details(int id)
{
return Content("Product details for #" + id);
}
}

Notice especially the Details methods - it has a parameter called "id", which matches the parameter we specified in the route. This means that if you call the Details URL, the ID you append to it will automatically be passed as the id parameter to the Details method!

As you can see, the routing template system is very flexible and allows you to support many different URL variations. There are many more possibilities, which we'll look into later in this tutorial, but hopefully these examples have given you a good idea on just how powerful the routing mechanisms in ASP.NET Core MVC is.

Multiple routes

As previously mentioned, you can of course have more than one route in your application. In fact, the most common approach is to have a default/catch-all route, as we saw in the previous article, but keep it in the bottom of the list of routes. Why? Because the routing rules are processed from the top and down, until a match is found - when a match occurs, no more routes are processed. In other words, you should have your most specific rules at the top, and then keep the less specific/catch-all route(s) at the bottom. It could look like this:

app.UseMvc(routes =>  
{  
routes.MapRoute("Products", "Products/{action=Index}/{id?}", new { controller = "Products" });  
routes.MapRoute("Default", "{controller=Home}/{action=Index}/{id?}");  
});

Notice how I have a very specific rule first (for products), and then the default, catch-all rule in the end. It makes sure that even if you haven't defined a specific route to a Controller/Action combination, it will try to reach it if it exists - if it doesn't exist, it will fallback to the Index() method on the HomeController.

Summary

By now, you should have a much better idea about how routing works and how to define your own routes. During the next articles, we will look into more available options you have when defining your routes, as well as other ways of using the routing mechanisms of ASP.NET MVC.