Friday, 30 October 2026

Conventional vs. attribute routing?

With conventional routing, you map out all your URL patterns in one central route table. Attribute routing, on the other hand, lets you drop those rules directly onto the controller and action itself. The examples here use older tech like ASP.NET MVC 5 and Web API 2, but don't worry—the exact same concept applies to ASP.NET Core.

Conventional routing:-

Patterns are registered once at startup file and apply to many controllers by naming convention.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}

/Products/Details/5 goes to ProductsController.Details(5). Add a new controller and its actions follow the same pattern with no extra routing code.

Attribute routing:-

The URL is declared next to the code that handles it.

// RouteConfig: enable it BEFORE the conventional routes
routes.MapMvcAttributeRoutes();
// Web API 2: config.MapHttpAttributeRoutes();

[RoutePrefix("orders")]
public class OrdersController : Controller
{
    [Route("")]                              // /orders
    public ActionResult Index() { return View(); }

    [Route("{id:int}")]                      // /orders/42
    public ActionResult Details(int id) { return View(); }

    [Route("{id:int}/items/{itemId:int}")]   // /orders/42/items/7
    public ActionResult Item(int id, int itemId) { return View(); }
}

Differences:-

Conventional Attribute
Where defined Central route table (RouteConfig) On controllers and actions
Style One pattern serves many controllers One URL per action, explicit
URL flexibility Best for uniform shapes like {controller}/{action}/{id} Handles irregular, nested, or custom URLs easily
Readability You must check the route table to know a URL The URL is visible right on the action
Maintenance Change one rule to affect many actions Edit each action, but changes stay local
Refactoring risk Renaming a controller or action changes its URL URLs stay stable when you rename methods
Constraints Defined in the route's constraints object Inline, such as {id:int}, {name:alpha}
Order First match in the table wins, so order matters Rarely order-sensitive, but ambiguity still errors
Best for Classic MVC sites with page-like URLs REST APIs, nested resources, SEO-friendly URLs

Why attribute routing is often preferred for APIs

  • Nested resources such as /customers/{customerId}/orders/{orderId} are awkward to express with a global pattern.
  • Stable public URLs. Renaming GetOrder to FetchOrder does not change /orders/{id}.
  • Optional parameters and defaults can be written inline: [Route("archive/{year:int?}")].
  • Route names can be set for URL generation: [Route("{id}", Name = "OrderById")].
  • Multiple URLs for one action are possible by stacking attributes.

When conventional routing still fits

  • A traditional content site where nearly every page follows {controller}/{action}/{id}.
  • Many controllers that should behave uniformly, with no need to repeat a route on each one.
  • A default homepage and catch-all fallbacks in a single place.

You can mix both

A common setup uses attribute routes for specific or API-style URLs and a conventional default route for everything else. Register attribute routes first so they take priority:

routes.MapMvcAttributeRoutes();   // specific routes first

routes.MapRoute(                  // general fallback last
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });

Common mistakes

  • Forgetting MapMvcAttributeRoutes(). The [Route] attributes are silently ignored.
  • Calling it after the conventional route. The general route can catch requests first.
  • Two actions matching the same URL and verb. This throws an "ambiguous match" error. Use constraints or different templates.
  • Missing [RoutePrefix] or typos in prefixes. URLs end up different from what you intended.
  • Mixing with Web API. Web API has its own route table, so MapHttpAttributeRoutes() is separate from MapMvcAttributeRoutes().

In ASP.NET Core

The same two ideas exist. Conventional routing uses MapControllerRoute, and attribute routing uses [Route], [HttpGet("...")], and similar attributes. One key difference is that in Core, an action that has route attributes is not reachable through conventional routes. Each action uses one style or the other.

Which one to choose?

  • Building an API? Use attribute routing.
  • Classic MVC site with uniform URLs? Conventional routing is fine and less repetitive.
  • Need both? Use attribute routes for the special cases and a conventional default as the fallback.


No comments:

Post a Comment

Note: only a member of this blog may post a comment.