Backend•Jun 2026•3 min read

Attribute Routing vs Convention Based Routing

Two ways to wire URLs to actions in ASP.NET Core. One puts the route next to the code it controls. The other hides it in a startup file three folders away.

The short answer

Attribute Routing over Convention Based Routing for most cases. The route belongs next to the action it serves.

  • Pick Attribute Routing if building an API, a REST surface, or anything where each endpoint's URL is a deliberate contract — put the [HttpGet("...")] on the action and never wonder where a route came from
  • Pick Convention Based Routing if shipping a classic server-rendered MVC app with a uniform /controller/action/id shape and genuinely want one rule to cover everything — convention earns its keep when the URLs are boring on purpose
  • Also consider: They coexist. Real apps use convention for the MVC view layer and attribute routing for the API controllers. The mistake is pretending you must choose globally.

— Nice Pick, opinionated tool recommendations

Where the truth lives

Attribute routing keeps the route on the action: [HttpGet("orders/{id}")] sits inches from the method that answers it. You read one file and you know the URL. Convention-based routing exiles that truth to MapControllerRoute in Program.cs, a single pattern that quietly dictates the address of every controller in the project. That centralization sounds tidy until you're staring at a 404 and the answer isn't in the controller, it's in a startup file you forgot existed. Locality of behavior wins debugging sessions. The convention crowd calls attributes "repetitive" — it's not repetition, it's each endpoint declaring its own contract instead of inheriting one by coincidence. When a route is wrong, attribute routing tells you exactly which line lied. Convention makes you reverse-engineer a template against a class name and pray the pluralizer agreed with you.

REST and ambiguity

Convention-based routing assumes URLs mirror your class structure: /Products/Edit/5. The moment you want REST — GET, POST, PUT, DELETE all hitting /api/products/5 with different verbs — convention falls apart, because it routes on action names, not HTTP methods. You end up bolting [HttpPost] attributes on anyway, which means you're already doing attribute routing and lying about it. Attribute routing was built for exactly this: verb plus template, no ceremony. Versioned APIs (/api/v2/...), nested resources (/customers/{id}/orders), constrained parameters ({id:int}) are one-liners on the action. Try expressing /customers/{id}/orders/{orderId} as a single convention template and watch it metastasize into three overlapping MapControllerRoute calls with order-of-registration bugs. If your URLs are a public API, ambiguity is a defect, and convention manufactures ambiguity by design.

Refactoring and failure modes

Rename a controller under convention-based routing and your URLs silently change — no compile error, no warning, just a different address and a broken client. The route was derived from a class name you were free to refactor, so the URL is hostage to your IDE's rename key. Attribute routing decouples them: the template is an explicit string, rename the class all you like, the URL holds. The trade is real — attribute strings aren't compile-checked either, and a typo'd template is its own 404. But it's a local typo on one line, not a global behavior shift triggered by an unrelated refactor. Convention's failure mode is spooky action at a distance; attribute's failure mode is a visible string you can grep. One you fix in five seconds. The other you discover in production when a partner's integration stops resolving and nobody touched the routing file.

When convention actually earns it

I don't hand out participation trophies, but convention-based routing isn't worthless. For a server-rendered MVC app with dozens of controllers that genuinely all follow /controller/action/{id?}, one template does the work of a hundred attributes, and adding a new controller costs zero routing code — it just works. That uniformity is a feature when the URLs are intentionally unremarkable: admin panels, internal CRUD tools, scaffolded dashboards. The DRY argument is legitimate there. Where it collapses is the instant one endpoint wants a different shape — and one always does. Then you mix in attributes, and now your routing logic lives in two places, which is worse than either pure approach. So convention is fine until it isn't, and it stops being fine early. Use it for the boring MVC majority, reach for attributes the moment a URL needs an opinion. Don't pretend the boring case is the whole app.

Quick Comparison

FactorAttribute RoutingConvention Based Routing
Route localityOn the action, read in one placeCentralized in Program.cs, far from the code
REST / verb-based APIsNative — verb + template per actionAwkward; forces attribute fallback anyway
Refactor safetyExplicit string survives class renamesURL silently tracks controller/action names
Boilerplate for uniform CRUDOne attribute per actionOne template covers every controller
Debugging a 404Greppable line on the actionReverse-engineer a global template

The Verdict

Use Attribute Routing if: You're building an API, a REST surface, or anything where each endpoint's URL is a deliberate contract — put the [HttpGet("...")] on the action and never wonder where a route came from.

Use Convention Based Routing if: You're shipping a classic server-rendered MVC app with a uniform /controller/action/id shape and genuinely want one rule to cover everything — convention earns its keep when the URLs are boring on purpose.

Consider: They coexist. Real apps use convention for the MVC view layer and attribute routing for the API controllers. The mistake is pretending you must choose globally.

Attribute Routing vs Convention Based Routing: FAQ

Is Attribute Routing or Convention Based Routing better?

Attribute Routing is the Nice Pick. The route belongs next to the action it serves. Attribute routing puts it there, makes REST APIs unambiguous, and survives refactors. Convention-based routing is action-at-a-distance: a single template in Program.cs silently governs hundreds of endpoints, and the failure mode is a 404 you debug by guessing.

When should you use Attribute Routing?

You're building an API, a REST surface, or anything where each endpoint's URL is a deliberate contract — put the [HttpGet("...")] on the action and never wonder where a route came from.

When should you use Convention Based Routing?

You're shipping a classic server-rendered MVC app with a uniform /controller/action/id shape and genuinely want one rule to cover everything — convention earns its keep when the URLs are boring on purpose.

What's the main difference between Attribute Routing and Convention Based Routing?

Two ways to wire URLs to actions in ASP.NET Core. One puts the route next to the code it controls. The other hides it in a startup file three folders away.

How do Attribute Routing and Convention Based Routing compare on route locality?

Attribute Routing: On the action, read in one place. Convention Based Routing: Centralized in Program.cs, far from the code. Attribute Routing wins here.

Are there alternatives to consider beyond Attribute Routing and Convention Based Routing?

They coexist. Real apps use convention for the MVC view layer and attribute routing for the API controllers. The mistake is pretending you must choose globally.

🧊
The Bottom Line
Attribute Routing wins

The route belongs next to the action it serves. Attribute routing puts it there, makes REST APIs unambiguous, and survives refactors. Convention-based routing is action-at-a-distance: a single template in Program.cs silently governs hundreds of endpoints, and the failure mode is a 404 you debug by guessing.

Related Comparisons

Disagree? nice@nicepick.dev