Declaring Routes with Attributes
Attribute routing lets you specify a route template directly on a controller or action using attributes like [Route("products")] on the class and [HttpGet("{id:int}")] on a method, rather than relying on a single centrally registered convention-based template to infer the URL shape from the controller and action names. When a [Route] attribute is applied to the controller class, action-level route templates are typically treated as relative to it, so [Route("products")] on ProductController combined with [HttpGet("{id:int}/reviews")] on a GetReviews method produces the combined path products/{id:int}/reviews, giving fine-grained control over exactly what each endpoint's URL looks like independent of the class or method names chosen in code.
Cricket analogy: It is like a stadium assigning each specific gate a hand-picked entrance number and section label rather than relying on a generic numbering scheme derived from the stand's construction order, giving ground staff exact control over wayfinding.
Route Tokens and Combining with Verb Attributes
Attribute routing supports the special tokens [controller] and [action], which are replaced with the controller name (suffix stripped) and action method name respectively, so [Route("[controller]/[action]")] on a class produces behavior similar to convention-based routing but scoped to just that controller, which is useful when most of an application uses fully custom attribute routes but a few controllers still want name-derived URLs. HTTP-verb attributes such as [HttpGet], [HttpPost], [HttpPut], [HttpDelete], and [HttpPatch] can each accept an optional template argument that acts as both the route template for that action and an implicit restriction to that verb, meaning [HttpPost("{id:int}/approve")] both defines the URL shape and ensures only POST requests match it, removing the need for a separate explicit [Route] attribute on that method in most cases.
Cricket analogy: It is like a scoreboard operator using shorthand codes that auto-expand to the batter's and bowler's actual names for that specific match, saving manual re-entry while still producing a fully specific display.
[Route("api/products")]
[ApiController]
public class ProductsController : ControllerBase
{
[HttpGet]
public IActionResult GetAll() => Ok(_repo.GetAll());
[HttpGet("{id:int}")]
public IActionResult GetById(int id)
{
var product = _repo.Find(id);
return product is null ? NotFound() : Ok(product);
}
[HttpGet("{id:int}/reviews")]
public IActionResult GetReviews(int id) => Ok(_repo.GetReviews(id));
[HttpPost]
public IActionResult Create(ProductDto dto) =>
CreatedAtAction(nameof(GetById), new { id = dto.Id }, dto);
}You can prefix every route in a controller with a version segment using [Route("api/v{version:apiVersion}/products")] combined with API versioning packages, or simply a literal like [Route("api/v2/products")], keeping versioning concerns out of every individual action's route template.
Mixing Attribute and Convention-Based Routing
A single ASP.NET Core application can use both routing styles simultaneously: controllers decorated with at least one [Route] or Http-verb template attribute on any action opt into attribute routing for that entire controller, while controllers without any routing attributes continue to be matched by the conventional routes registered via app.MapControllerRoute. This is common in real applications where API controllers use precise attribute routes like api/v1/orders/{id:int} for predictable REST endpoints, while traditional MVC controllers serving Razor views rely on the flexible convention-based {controller}/{action}/{id?} default, and the two systems coexist without conflict because the framework evaluates a controller's own attribute routes before falling back to conventional route matching for that controller.
Cricket analogy: It is like a cricket board running both a tightly scheduled international Test series with fixed venues and dates alongside a flexible domestic T20 league that follows a rotating round-robin format, both operating under the same governing body without conflict.
A controller cannot mix attribute-routed and convention-routed actions inconsistently in a way that relies on the convention route matching an attribute-routed controller's actions: once any action on a controller has a route attribute, that controller is fully opted into attribute routing, and any action on it without its own route template becomes unreachable via the conventional default route.
- Attribute routing declares route templates directly via [Route] on the class and [HttpGet]/[HttpPost]/etc. on actions.
- Controller-level [Route] templates combine with action-level templates to build the full path.
- The [controller] and [action] tokens let attribute routes still derive segments from class/method names when desired.
- HTTP-verb attributes like [HttpPost("{id:int}/approve")] define both the route template and the allowed verb in one declaration.
- A single application can mix attribute routing and convention-based routing across different controllers.
- Once any action on a controller uses a route attribute, that entire controller is opted into attribute routing.
- Attribute routing is the standard approach for Web APIs because it gives precise, predictable REST-style URLs.
Practice what you learned
1. What happens when [Route("products")] is applied to a controller class and [HttpGet("{id:int}")] to one of its actions?
2. What do the [controller] and [action] tokens do in an attribute route template?
3. What does supplying a template to an HTTP-verb attribute, like [HttpPost("{id:int}/approve")], accomplish?
4. Can attribute routing and convention-based routing be used in the same application?
5. What happens to an action without its own route template once another action on the same controller has a route attribute?
Was this page helpful?
You May Also Like
Routing in MVC
See how convention-based routing maps incoming URLs to controller actions using route templates, defaults, and constraints.
Action Methods and Action Results
Understand how public controller methods become callable actions and how the IActionResult family shapes the HTTP response returned to the client.
Controllers Basics
Learn what a controller is in ASP.NET MVC, how it is discovered and instantiated, and how it coordinates models and views to handle a request.
Related Reading
Related Study Notes in Microsoft Technologies
Browse all study notesWindows 10 / UWP Development Study Notes
.NET · 30 topics
Microsoft TechnologiesWindows Batch Scripting Study Notes
Batch · 30 topics
Microsoft TechnologiesMFC (Microsoft Foundation Classes) Study Notes
C++ · 30 topics
Microsoft TechnologiesSilverlight Study Notes
.NET · 30 topics
Microsoft TechnologiesXAML Study Notes
.NET · 30 topics
Microsoft TechnologiesWPF (Windows Presentation Foundation) Study Notes
.NET · 30 topics