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
GetOrdertoFetchOrderdoes 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 fromMapMvcAttributeRoutes().
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.