ASP.NET Core MVC Routing
Introduction to Routing
Routing is the process of mapping incoming HTTP requests to the appropriate MVC controller actions or Razor Pages. In ASP.NET Core MVC, routing is highly configurable and allows you to define complex URL structures that are both user-friendly and search-engine-optimized.
The core of ASP.NET Core routing is the routing middleware, which inspects the request URL and the application's defined routes to determine how to handle the request. This process involves matching the URL against a set of route patterns.
Route Templates
Route templates are strings that define the structure of URLs. They can include literal values and parameters. Parameters are enclosed in curly braces ({}
) and act as placeholders for values in the URL.
For example, a route template like products/{category}/{id}
would match URLs such as /products/electronics/123
. The values electronics
and 123
would be extracted and passed as arguments to the action method.
Default Route Template
The default route template in ASP.NET Core MVC often looks like this:
"/{controller=Home}/{action=Index}/{id?}"
{controller=Home}
: Specifies the controller name. If omitted in the URL, it defaults to theHome
controller.{action=Index}
: Specifies the action method name. If omitted, it defaults to theIndex
action.{id?}
: Specifies an optional parameter namedid
. The question mark makes it optional.
Route Constraints
You can apply constraints to route parameters to ensure that only values matching specific criteria are accepted. Constraints are typically defined as regular expressions.
Example of a route with a constraint for the id
parameter:
"products/{category}/{id:int}"
This route will only match URLs where the id
is an integer.
Common Constraints
int
: The value must be an integer.float
: The value must be a floating-point number.guid
: The value must be a GUID.alpha
: The value must contain only alphabetic characters.decimal
: The value must be a decimal number.max-length(n)
: The value must have a maximum length ofn
.min-length(n)
: The value must have a minimum length ofn
.
Attribute Routing vs. Convention-Based Routing
ASP.NET Core MVC supports two primary routing approaches:
Convention-Based Routing
This is the traditional approach where routes are defined globally in the Startup.cs
file (or Program.cs
in .NET 6+ minimal APIs). It relies on conventions and a central configuration.
// In Startup.cs or Program.cs
services.AddControllersWithViews();
// ...
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
Attribute Routing
Attribute routing allows you to define routes directly on your controller actions using attributes. This offers more flexibility and can make routes more self-contained and easier to understand within the controller itself.
Example: Attribute Routing
using Microsoft.AspNetCore.Mvc;
namespace MyProject.Controllers
{
public class OrdersController : Controller
{
[Route("api/orders")]
public IActionResult GetOrders()
{
// ... logic to get orders
return Ok("List of orders");
}
[Route("api/orders/{id:int}")]
public IActionResult GetOrderById(int id)
{
// ... logic to get order by id
return Ok($"Order details for ID: {id}");
}
}
}
To enable attribute routing, ensure you have:
// In Startup.cs or Program.cs
services.AddControllersWithViews();
// ...
app.UseRouting(); // Ensure UseRouting is called before UseEndpoints
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers(); // For attribute routing
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}"); // For convention-based routing
});
Route Parameters and Action Method Arguments
Route parameters are automatically mapped to the parameters of your action methods based on their names. If the parameter names match, ASP.NET Core handles the binding automatically.
If parameter names don't match, or for more complex scenarios, you can use the [FromRoute]
attribute:
[Route("items/{itemId}")]
public IActionResult GetItemDetails([FromRoute] int itemId)
{
// ...
return Ok($"Details for item {itemId}");
}
Creating Custom Route Constraints
For more advanced scenarios, you can create custom route constraints by implementing the IRouteConstraint
interface.
Example: Custom Constraint for Specific Keywords
Define a constraint:
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Http;
using System.Linq;
public class AllowedCategoryConstraint : IRouteConstraint
{
private readonly string[] _allowedCategories;
public AllowedCategoryConstraint(params string[] allowedCategories)
{
_allowedCategories = allowedCategories.Select(c => c.ToLower()).ToArray();
}
public bool Match(HttpContext? httpContext, IRouter? route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
{
if (values.TryGetValue(routeKey, out object? value) && value != null)
{
return _allowedCategories.Contains(value.ToString()?.ToLower());
}
return false;
}
}
Register and use the constraint:
// In Startup.cs or Program.cs
services.AddRouting(options =>
{
options.ConstraintMap.Add("allowedcat", typeof(AllowedCategoryConstraint));
});
// In your route configuration (e.g., Program.cs)
endpoints.MapControllerRoute(
name: "productByCategory",
pattern: "products/{category:allowedcat}",
defaults: new { controller = "Products", action = "ListByCategory" },
constraints: new { category = new AllowedCategoryConstraint("electronics", "books", "clothing") }
);
Summary
Understanding and effectively utilizing ASP.NET Core MVC routing is crucial for building robust, maintainable, and user-friendly web applications. Whether you choose convention-based routing, attribute routing, or a combination of both, mastering route templates, constraints, and parameter binding will empower you to create elegant URL structures.