๐ค๏ธ ASP.NET Routing: The GPS of Your Web App
Imagine youโre a mail carrier. Every house has an address. Routing in ASP.NET is like having a super-smart GPS that knows exactly which โhouseโ (controller action) to deliver each โletterโ (HTTP request) to.
๐ฏ What is Routing?
Think of your web app as a big city. Users send requests (like asking for directions). Routing is the city map that tells each request exactly where to go.
Without routing: Chaos! Requests get lost. With routing: Every request finds its home.
User types: /products/shoes/42
Routing says: "Go to ProductsController, GetDetails action, ID = 42"
๐๏ธ Routing Fundamentals
Routing matches URLs to code. Thatโs it!
The Simple Rule
URL Pattern โ Controller โ Action โ Result
Example:
// URL: /hello
// Goes to: HomeController.SayHello()
public class HomeController
{
public string SayHello()
{
return "Hello, World!";
}
}
Key Pieces
| Piece | What It Does |
|---|---|
| Route Template | The pattern to match (like /products/{id}) |
| Route Values | Data extracted from URL (like id = 42) |
| Endpoint | Where the request lands (your action method) |
๐ช Endpoint Routing
The Modern Way (ASP.NET Core 3.0+)
Think of endpoints as destinations on a map. Endpoint routing picks the destination FIRST, then travels there.
graph TD A["HTTP Request"] --> B["Routing Middleware"] B --> C["Pick Endpoint"] C --> D["Run Other Middleware"] D --> E["Execute Endpoint"]
Why Itโs Better
Old way: Figured out destination at the very end. New way: Knows destination early, so other middleware can prepare.
Setup Example:
var app = builder.Build();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
The magic: UseRouting() picks the endpoint. MapControllers() registers your endpoints.
๐ฃ๏ธ Conventional Routing
The Pattern-Based Approach
Like having a standard address format for your whole city:
Street Name / House Number / Apartment
Controller / Action / ID
Default Convention
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}"
);
What this means:
| URL | Controller | Action | ID |
|---|---|---|---|
/ |
Home | Index | null |
/Products |
Products | Index | null |
/Products/Details |
Products | Details | null |
/Products/Details/5 |
Products | Details | 5 |
Real Example
// Route: /Blog/Post/42
public class BlogController : Controller
{
public IActionResult Post(int id)
{
// id = 42
return View();
}
}
Remember: Conventional = Define patterns once, use everywhere.
๐ท๏ธ Attribute Routing
The Label-on-Each-Door Approach
Instead of one big map, you put a label directly on each door (action).
[Route("api/[controller]")]
public class BooksController : Controller
{
[HttpGet("{id}")]
public Book GetBook(int id)
{
return FindBook(id);
}
[HttpPost]
public void CreateBook(Book book)
{
SaveBook(book);
}
}
Result
| HTTP Method | URL | Action |
|---|---|---|
| GET | /api/books/5 |
GetBook(5) |
| POST | /api/books |
CreateBook |
Token Replacement
Special placeholders that auto-fill:
| Token | Replaced With |
|---|---|
[controller] |
Controller name (minus โControllerโ) |
[action] |
Action method name |
[area] |
Area name |
[Route("[controller]/[action]")]
public class CatalogController : Controller
{
public IActionResult List()
{
// URL: /Catalog/List
}
}
๐ Route Templates
The Address Pattern Language
Route templates are like fill-in-the-blank addresses.
Building Blocks
/products/{category}/{id}
โ โ โ
literal parameter parameter
Examples
| Template | Matches | Extracts |
|---|---|---|
products/{id} |
/products/42 |
id = 42 |
{controller}/{action} |
/home/about |
controller=home, action=about |
files/{*path} |
/files/docs/readme.txt |
path = docs/readme.txt |
Optional Parameters
Add ? to make a parameter optional:
[Route("search/{query?}")]
public IActionResult Search(string query)
{
// /search โ query = null
// /search/cats โ query = "cats"
}
Default Values
Add =value to set defaults:
[Route("page/{num=1}")]
public IActionResult Page(int num)
{
// /page โ num = 1
// /page/5 โ num = 5
}
Catch-All Parameters
Use * or ** to grab everything:
[Route("files/{**path}")]
public IActionResult GetFile(string path)
{
// /files/images/logo.png
// path = "images/logo.png"
}
๐ง Route Constraints
The Bouncer at the Door
Constraints check if a value is allowed BEFORE the request enters.
Built-in Constraints
| Constraint | Matches | Example |
|---|---|---|
int |
Integers | {id:int} โ 42 โ, โabcโ โ |
bool |
true/false | {active:bool} โ true โ |
datetime |
Dates | {date:datetime} |
guid |
GUIDs | {id:guid} |
minlength(n) |
Min string length | {name:minlength(3)} |
maxlength(n) |
Max string length | {name:maxlength(20)} |
min(n) |
Min number | {age:min(18)} |
max(n) |
Max number | {qty:max(100)} |
range(m,n) |
Number range | {page:range(1,100)} |
alpha |
Letters only | {name:alpha} |
regex(exp) |
Custom pattern | {code:regex(^[A-Z]{{3}}$)} |
Combining Constraints
Use : to chain them:
[Route("users/{id:int:min(1)}")]
public IActionResult GetUser(int id)
{
// Only matches positive integers
}
Real Example
[Route("products/{category:alpha}/{id:int:range(1,10000)}")]
public IActionResult Product(string category, int id)
{
// /products/electronics/42 โ
// /products/123/42 โ (category not alpha)
// /products/toys/0 โ (id not in range)
}
๐ URL Generation
Building Addresses, Not Just Reading Them
Instead of hardcoding URLs, let the routing system build them for you!
Why It Matters
โ Hardcoded (breaks when routes change):
<a href="/products/details/5">View</a>
โ Generated (always correct):
Url.Action("Details", "Products", new { id = 5 })
// Output: "/products/details/5"
Methods for URL Generation
In Controllers:
// Generate URL string
string url = Url.Action("Index", "Home");
// Generate with route values
string url = Url.Action("Details", "Products",
new { id = 42, color = "red" });
// Result: /Products/Details/42?color=red
Using Route Names:
// Define a named route
[Route("p/{id}", Name = "ProductDetail")]
public IActionResult Details(int id) { }
// Generate URL by name
string url = Url.RouteUrl("ProductDetail",
new { id = 42 });
// Result: /p/42
LinkGenerator Service
For generating URLs outside of controllers:
public class EmailService
{
private readonly LinkGenerator _links;
public EmailService(LinkGenerator links)
{
_links = links;
}
public string GetProductLink(int id)
{
return _links.GetPathByAction(
"Details",
"Products",
new { id });
}
}
๐ Link Generation in Views
Making Clickable Links
Tag Helpers (Razor Views)
<!-- Simple link -->
<a asp-controller="Products"
asp-action="Details"
asp-route-id="42">
View Product
</a>
<!-- Output: <a href="/Products/Details/42">View Product</a> -->
Using Named Routes
<a asp-route="ProductDetail"
asp-route-id="42">
View
</a>
Form with Route Values
<form asp-controller="Products"
asp-action="Create"
method="post">
<!-- form fields -->
</form>
Dynamic Links in Code
// In controller
return RedirectToAction("Index", "Home");
// With route values
return RedirectToAction("Details", "Products",
new { id = 42 });
// Using route name
return RedirectToRoute("ProductDetail",
new { id = 42 });
๐ฏ Quick Summary
graph TD A["URL Request"] --> B{Routing System} B --> C["Match Template"] C --> D["Apply Constraints"] D --> E["Extract Values"] E --> F["Find Endpoint"] F --> G["Execute Action"] H["Link Generation"] --> I["Build URLs"] I --> J["Safe & Consistent"]
The Big Picture
| Concept | Think of it asโฆ |
|---|---|
| Routing Fundamentals | City map basics |
| Endpoint Routing | GPS picking destination first |
| Conventional | Standard address format |
| Attribute | Labels on each door |
| Templates | Fill-in-the-blank addresses |
| Constraints | Bouncers checking IDs |
| URL Generation | Automatic address builder |
| Link Generation | Making clickable directions |
๐ช Youโve Got This!
Routing is just matching patterns and extracting values. Start with conventions, add attributes where needed, and let the framework generate your URLs.
One last tip: When in doubt, use [Route] attributes on API controllers. Theyโre explicit and easy to read!
๐ Now go build amazing web apps with perfect routing!
