TOC
Routing:

Attribute routing

In the previous article, we discussed what a route is, how it works and how you can define routes in the Startup.cs files. This is how routes are defined in a lot of projects, but there is an alternative: ASP.NET MVC allows you to define a route directly on the responsible Controller and/or specifically for the responsible action/method. This is often called Attribute routing, because you use attributes to define the route for the method. Allow me to illustrate with an example:

[Route("/products")]  
public class ProductController : Controller  
{
    public IActionResult Index()  
    {  
return Content("Products Index");  
    }  
}

Pay attention to the very first line of the example, where I use the Route() attribute method to define a routing template for this controller. You will notice that my controller uses the word "product" in the singular form, but I would like to use the plural form for the URL's. I can accomplish just that by defining this word as the routing template for the Controller - with that in place, I can now access URL's like /products/ or /products, since they will just default to our Index method.

That was very basic and only works because it automatically falls back to the Index method, but attribute routing allows for much more complicated use-cases as well. For instance, our Controller could also be used to serve a single product instead of the general index/overview. Let's add a method for that, and define an access route for it at the same time:

[Route("/products")]
public class ProductController : Controller
{      
public IActionResult Index()
{
return Content("Products Index");
}

[Route("{id}")]
public IActionResult Details(int id)
{
return Content("Product #" + id);
}
}

Notice how I have added a Route Attribute to the Details method. It uses a special syntax for defining variable content, where we surround the name of the parameter with a set of curly braces. We will discuss this more in depth later in this chapter, but for now, just be aware that it allows us to specify a parameter which will be passed to the responding method. What it means in this case is that we could now load the product with the ID passed through the URL and display it to the user (in the example, we just print the ID though, to keep things simple). Our Controller now supports URL's like this one: /products/42 and /products/84/ (so the last slash is optional).

Please notice that I only use the last part of the URL in the route template (the ID) because the Controller already specifies that it should be called with the word products first. If that wasn't the case, we would have to define the entire URL for this specific method instead, like this:

[Route("/products/{id}")]
public IActionResult Details(int id)
{
return Content("Product #" + id);
}

So, whether you define it partially for the Controller and partially for the method, or specify it all on each method, is really up to you and the situation. It could be useful to define it all on each method if your Controller should support more than one entrance URL, but in most situations, you could probably save a few keystrokes by defining a common entrance for the Controller and then the rest for each method.

For some situations, you may want a method to have more than one route. This is easily accomplished, because you can define as many as you want. You can even mix and match URL's partially and completely defined on the specific method, like this:

[Route("/products")]
public class ProductController : Controller
{      
public IActionResult Index()
{
return Content("Products Index");
}

[Route("{id}")]
[Route("/product/{id}")]
public IActionResult Details(int id)
{
return Content("Product #" + id);
}
}

With that in place, our product details page can now be accessed using URL's with both the singular and the plural form of the word "product". This specific result could be accomplished in other ways, with a more flexible route, but it serves as a nice demonstration of how easy it is to define multiple routes.

Summary

Attribute routing can be a great alternative to the conventional way of defining routes in your project. Because they are defined directly on the Controller, it can be easier to understand which routes are valid for a given Controller and method. On the other hand, there are a few limitations when using Attribute routing, when it comes to routing constraints, but for most situations, this shouldn't be a problem. If you run into these limitations anyway, remember that you are free to combine conventional routing with Attribute routing.

So, at the end of the day, whether to use Attribute routing or not comes down to personal taste and the requirements of the project you are working on.