Attribute routing in MVC is a method of defining routes directly on your action methods and controllers using attributes. It provides a powerful and intuitive way to map incoming Uniform Resource Identifiers (URIs) to specific controller actions, giving developers fine-grained control over the structure of their web application's URLs.
As its name indicates, attribute routing uses attributes to specify routes. This approach contrasts with the traditional convention-based routing (route tables), where routes are defined centrally in a configuration file. Routing is how ASP.NET MVC associates a URI with an action. MVC 5 introduced this new type of routing, and it has since become a preferred method for many developers due to its flexibility and readability.
Why Use Attribute Routing?
Attribute Routing provides you with more control over the URIs of your web application, offering several significant advantages:
- Clearer URI Definitions: Routes are defined right next to the code they map to, making it easy to understand which URI triggers a specific action.
- Enhanced Readability: Developers can quickly grasp the URI structure by looking at the controller and action methods directly.
- Better Control over URLs: It allows for the creation of more descriptive and user-friendly URLs, which can positively impact Search Engine Optimization (SEO).
- Reduced Ambiguity: By associating routes directly with actions, it minimizes conflicts and makes complex routing scenarios more manageable.
- Support for RESTful APIs: It's particularly well-suited for building RESTful APIs where URIs often need to be precise and reflect resource hierarchies.
How Attribute Routing Works
Attribute routing primarily uses the [Route]
attribute, which can be applied to controllers, action methods, or even both simultaneously.
The [Route]
Attribute
The [Route]
attribute specifies the URI template for an action or a controller.
-
On an Action Method:
public class ProductsController : Controller { [Route("products/{id}")] public ActionResult Details(int id) { // Logic to get product details by ID return View(); } [Route("products/category/{categoryName}")] public ActionResult ByCategory(string categoryName) { // Logic to get products by category return View(); } }
In this example:
GET /products/123
would map to theDetails
action.GET /products/category/electronics
would map to theByCategory
action.
-
On a Controller (with Route Prefix):
You can also apply the[RoutePrefix]
attribute to a controller to define a common prefix for all actions within that controller. This helps keep routes DRY (Don't Repeat Yourself).[RoutePrefix("api/v1/users")] public class UsersController : Controller { // GET /api/v1/users [Route("")] public ActionResult GetAllUsers() { // Logic to get all users return View(); } // GET /api/v1/users/{id} [Route("{id:int}")] // Constraint: id must be an integer public ActionResult GetUserById(int id) { // Logic to get user by ID return View(); } // POST /api/v1/users [Route("")] [HttpPost] public ActionResult CreateUser([FromBody] User user) { // Logic to create a new user return View(); } }
Here, the
[RoutePrefix("api/v1/users")]
applies a base URI segment to all actions within theUsersController
. The[Route("")]
then indicates the base path (e.g.,/api/v1/users
), and[Route("{id:int}")]
defines a path with a parameter and a constraint.
Route Constraints
Attribute routing supports route constraints, which allow you to restrict how parameters in the URI template are matched. Common constraints include:
{id:int}
: Parameterid
must be an integer.{year:datetime}
: Parameteryear
must be a valid date/time.{name:alpha}
: Parametername
must contain only alphabetic characters.{id:guid}
: Parameterid
must be a GUID.
Optional URI Parameters
Parameters can be made optional by adding a question mark ?
after them:
[Route("products/search/{query?}")]
public ActionResult Search(string query)
{
// Logic to search products. query can be null if not provided.
return View();
}
GET /products/search
and GET /products/search/keyword
would both map to this action.
Enabling Attribute Routing
To use attribute routing in your ASP.NET MVC 5+ application, you need to enable it during your application's startup. This is typically done in the RouteConfig.cs
file (or Global.asax.cs
for older projects) by calling MapMvcAttributeRoutes()
:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// Enable attribute routing
routes.MapMvcAttributeRoutes();
// Optional: Still register convention-based routes if needed
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
It's important to place routes.MapMvcAttributeRoutes();
before any convention-based routes, as attribute routes are generally more specific and should be matched first.
Attribute Routing vs. Convention-Based Routing
While attribute routing offers significant advantages, it's helpful to understand its relationship with traditional convention-based routing.
Feature | Attribute Routing | Convention-Based Routing |
---|---|---|
Route Definition | Defined directly on controllers/actions via attributes. | Defined centrally in a RouteConfig file (route table). |
Control | High control over specific URI paths. | Less granular; relies on URL patterns. |
Readability | Routes are co-located with action logic, improving clarity. | Routes are separate from action logic, requiring context switching. |
Flexibility | Excellent for RESTful APIs and specific URL requirements. | Good for general, predictable URL structures. |
Maintenance | Easier to manage routes for individual actions/controllers. | Can become complex with many custom routes. |
You can use both types of routing within the same application. Attribute routes are generally processed first due to their specificity, allowing you to define custom, precise URLs while still relying on conventional routing for standard, less specific paths.