zaro

What is Attribute Routing in MVC?

Published in MVC Routing 5 mins read

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 the Details action.
    • GET /products/category/electronics would map to the ByCategory 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 the UsersController. 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}: Parameter id must be an integer.
  • {year:datetime}: Parameter year must be a valid date/time.
  • {name:alpha}: Parameter name must contain only alphabetic characters.
  • {id:guid}: Parameter id 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.