C# Routing
Routing is a fundamental concept in web development that determines how your application responds to client requests. In C# web development, particularly with ASP.NET Core, routing is the mechanism that maps incoming HTTP requests to specific action methods in your controllers.
Introduction to Routing
When a user navigates to a URL in your web application, the routing system determines which code should execute to generate the response. Think of routing as a traffic control system for your web application - it directs incoming requests to the right destination.
In ASP.NET Core applications, routing is highly configurable and offers various approaches depending on your needs. Whether you're building an MVC application, a Web API, or a Razor Pages app, understanding routing is essential for developing effective C# web applications.
Basic Routing Concepts
URL Patterns
Routing works with URL patterns that typically follow this structure:
https://example.com/{controller}/{action}/{id?}
Where:
{controller}
is the name of the controller (without the "Controller" suffix){action}
is the name of the action method within that controller{id?}
is an optional parameter (the question mark indicates it's optional)
Route Configuration
In ASP.NET Core, you typically configure routes in the Program.cs
file (or Startup.cs
in older versions). Here's a basic example:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
In this example, the default route pattern specifies that:
- If no controller is specified, use
HomeController
- If no action is specified, use the
Index
action - The
id
parameter is optional
Route Types in ASP.NET Core
Convention-based Routing
Convention-based routing establishes a convention for URL paths. This is the traditional approach used in ASP.NET MVC.
app.MapControllerRoute(
name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
In this example, we've added a custom route for blog articles before the default route. The order matters! Routes are evaluated in the order they're registered.
Attribute Routing
Attribute routing allows you to define routes directly on your controller classes or action methods using attributes.
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
[HttpGet] // GET: /api/products
public IActionResult GetAll()
{
// Return all products
return Ok(new[] { "Product1", "Product2", "Product3" });
}
[HttpGet("{id}")] // GET: /api/products/5
public IActionResult GetById(int id)
{
// Return product with specified ID
return Ok($"Product {id}");
}
[HttpPost] // POST: /api/products
public IActionResult Create([FromBody] ProductModel product)
{
// Create a new product
return CreatedAtAction(nameof(GetById), new { id = 123 }, product);
}
}
Attribute routing is especially common in Web API development and offers more flexibility for complex routing scenarios.
Route Constraints
Route constraints limit what values can match a route parameter. They help ensure that your routes only match the URLs you intend them to match.
app.MapControllerRoute(
name: "product",
pattern: "product/{id:int}",
defaults: new { controller = "Product", action = "Details" });
In this example, the id
parameter must be an integer for the route to match.
Common route constraints include:
Constraint | Example | Matches | Doesn't Match |
---|---|---|---|
int | {id:int} | 123 | abc |
bool | {active:bool} | true, false | yes, no |
datetime | {date:datetime} | 2023-10-15 | october-15 |
decimal | {price:decimal} | 19.99 | $19.99 |
min | {id:min(10)} | 15 | 5 |
alpha | {name:alpha} | abc | abc123 |
regex | {code:regex(^[a-z]{3}[0-9]{3}$)} | abc123 | 123abc |
Route Parameters and Data Binding
Routes can include parameters that are passed to your action methods.
[Route("users/{username}")]
public IActionResult UserProfile(string username)
{
// The username parameter comes from the route
return View(new UserProfileViewModel { Username = username });
}
ASP.NET Core can bind route parameters to your action method parameters automatically.
You can also use more complex model binding:
[HttpGet("search")]
public IActionResult Search([FromQuery] SearchCriteria criteria)
{
// criteria is bound from query string parameters
// e.g., /search?keyword=laptop&maxPrice=1000
var results = _productService.Search(criteria);
return View(results);
}
Practical Example: Building a Blog with Custom Routing
Let's implement a simple blog system with custom routing. We want URLs like:
/blog
- Blog index/blog/2023/10
- Posts from October 2023/blog/category/tech
- Posts in the "tech" category/blog/post/my-awesome-post
- Individual post with slug
First, let's create our controller:
public class BlogController : Controller
{
private readonly IBlogService _blogService;
public BlogController(IBlogService blogService)
{
_blogService = blogService;
}
[Route("blog")]
public IActionResult Index()
{
var posts = _blogService.GetRecentPosts();
return View(posts);
}
[Route("blog/{year:int}/{month:int:range(1,12)}")]
public IActionResult ByMonth(int year, int month)
{
var posts = _blogService.GetPostsByMonth(year, month);
return View("Index", posts);
}
[Route("blog/category/{category}")]
public IActionResult ByCategory(string category)
{
var posts = _blogService.GetPostsByCategory(category);
return View("Index", posts);
}
[Route("blog/post/{slug}")]
public IActionResult Post(string slug)
{
var post = _blogService.GetPostBySlug(slug);
if (post == null)
return NotFound();
return View(post);
}
}
Then, we register these routes in the application:
app.MapControllers(); // This enables attribute routing
Advanced Routing Features
Route Templates
Route templates can include:
- Literal segments:
/blog
- Parameter segments:
{id}
- Wildcard segments:
{*path}
Route Constraints with Regular Expressions
You can use regular expressions for more complex constraints:
[Route("products/{sku:regex(^[A-Z]{{3}}\\d{{4}}$)}")]
public IActionResult GetProductBySku(string sku)
{
// Only matches SKUs like "ABC1234"
return Ok($"Product SKU: {sku}");
}
Route Data Values
You can access route data in your action methods or views:
// In a controller action
var controller = RouteData.Values["controller"];
var action = RouteData.Values["action"];
<!-- In a Razor view -->
<p>Current controller: @ViewContext.RouteData.Values["controller"]</p>
URL Generation
You can generate URLs using the Url
helper in controllers or views:
// In a controller
var url = Url.Action("Details", "Product", new { id = 123 });
<!-- In a Razor view -->
<a asp-controller="Product" asp-action="Details" asp-route-id="123">View Product</a>
Common Routing Scenarios and Solutions
Localizing URLs
app.MapControllerRoute(
name: "localized",
pattern: "{culture}/{controller=Home}/{action=Index}/{id?}",
constraints: new { culture = new RegexRouteConstraint("^[a-z]{2}(-[a-z]{2})?$") });
This allows URLs like /en-us/products/details/5
.
Versioned APIs
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("2.0")]
public class ProductsController : ControllerBase
{
[HttpGet]
[MapToApiVersion("1.0")]
public IActionResult GetV1()
{
return Ok("Version 1.0");
}
[HttpGet]
[MapToApiVersion("2.0")]
public IActionResult GetV2()
{
return Ok("Version 2.0");
}
}
This allows you to have different implementations for different API versions.
Best Practices for Routing
-
Keep URLs Simple and RESTful
- Use nouns for resources
- Use HTTP verbs to indicate actions (GET, POST, PUT, DELETE)
- Keep URLs clean and human-readable
-
Order Your Routes Correctly
- More specific routes should come before more general routes
-
Use Attribute Routing for APIs
- Attribute routing is generally clearer for API endpoints
-
Avoid Route Parameter Naming Conflicts
- Don't use the same parameter name in different parts of the route
-
Use Meaningful Route Names
- When naming routes, choose descriptive names that reflect their purpose
-
Prefer Routing Over Query Strings for Major Resource Identifiers
- Use
/products/categories/5
rather than/products?categoryId=5
- Use
Summary
Routing is a crucial component of C# web development with ASP.NET Core. It determines how URLs map to your application's code and greatly affects the user experience of your web application.
In this guide, we've covered:
- Basic routing concepts and configuration
- Convention-based and attribute routing
- Route constraints and parameters
- Practical examples of implementing custom routes
- Advanced features and best practices
Understanding routing thoroughly will help you build more organized, maintainable, and user-friendly web applications.
Additional Resources and Exercises
Resources
- Official ASP.NET Core Routing Documentation
- ASP.NET Core MVC Routing Tutorial
- Attribute Routing in ASP.NET Core
Exercises
-
Basic Routing Configuration
- Create a new ASP.NET Core MVC application
- Configure a custom route for a "Blog" controller that supports year and month parameters
-
Route Constraints Practice
- Implement a product catalog with routes that enforce proper constraints on IDs and categories
-
RESTful API Routing
- Create a RESTful API for a resource of your choice (e.g., books, movies, recipes)
- Implement GET, POST, PUT, and DELETE endpoints with appropriate attribute routing
-
Advanced URL Generation
- Create a navigation system that dynamically generates URLs based on the current culture and user permissions
-
Slugified URLs
- Implement a news article system with SEO-friendly slugified URLs (e.g.,
/news/my-awesome-article
) instead of ID-based URLs
- Implement a news article system with SEO-friendly slugified URLs (e.g.,
By practicing these exercises, you'll gain hands-on experience with C# routing and be better prepared to implement effective routing in your real-world applications.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)