Introduction
ASP.NET MVC maps URL segments to controller action parameters through its routing system. The default route {controller}/{action}/{id} handles a single parameter, but many applications need multiple parameters in URLs — for example /Products/List/Electronics/1/50 for category, page, and page size. You can define custom route templates, use attribute routing, query strings, or a combination to pass multiple parameters to controller actions.
Default Route (Single Parameter)
1// RouteConfig.cs
2routes.MapRoute(
3 name: "Default",
4 url: "{controller}/{action}/{id}",
5 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
6);
1// Matches: /Products/Details/42
2public class ProductsController : Controller
3{
4 public ActionResult Details(int id)
5 {
6 var product = db.Products.Find(id);
7 return View(product);
8 }
9}
The default route only handles one parameter (id). For multiple parameters, you need custom routes or attribute routing.
Custom Route with Multiple Parameters
1// RouteConfig.cs — add BEFORE the default route
2routes.MapRoute(
3 name: "ProductsByCategory",
4 url: "Products/{category}/{page}/{pageSize}",
5 defaults: new { controller = "Products", action = "List", page = 1, pageSize = 20 }
6);
1// Matches: /Products/Electronics/2/50
2public class ProductsController : Controller
3{
4 public ActionResult List(string category, int page = 1, int pageSize = 20)
5 {
6 var products = db.Products
7 .Where(p => p.Category == category)
8 .Skip((page - 1) * pageSize)
9 .Take(pageSize)
10 .ToList();
11
12 return View(products);
13 }
14}
Route order matters — MVC evaluates routes top to bottom and uses the first match.
Attribute Routing (Recommended)
1// Enable attribute routing in RouteConfig.cs
2public static void RegisterRoutes(RouteCollection routes)
3{
4 routes.MapMvcAttributeRoutes(); // Enable attribute routing
5 // ... default route below
6}
1public class ProductsController : Controller
2{
3 // /Products/Electronics/2/50
4 [Route("Products/{category}/{page:int}/{pageSize:int}")]
5 public ActionResult List(string category, int page = 1, int pageSize = 20)
6 {
7 return View();
8 }
9
10 // /Products/42/Reviews
11 [Route("Products/{productId:int}/Reviews")]
12 public ActionResult Reviews(int productId)
13 {
14 return View();
15 }
16
17 // /Products/2024/01/15
18 [Route("Products/{year:int}/{month:int}/{day:int}")]
19 public ActionResult ByDate(int year, int month, int day)
20 {
21 return View();
22 }
23}
Attribute routing places the route directly on the action method, making it easier to read and maintain than centralized route tables.
Route Constraints
1// Constrain parameters to specific types or patterns
2[Route("Products/{id:int}")] // id must be integer
3[Route("Users/{username:alpha}")] // letters only
4[Route("Products/{id:min(1)}")] // minimum value
5[Route("Archive/{year:range(2000,2030)}")] // range constraint
6[Route("Files/{filename:regex(^.*\\.pdf$)}")] // regex constraint
7[Route("Api/{version:int:min(1):max(3)}")] // multiple constraints
| Constraint | Example | Matches |
int | {id:int} | 123, not abc |
alpha | {name:alpha} | abc, not 123 |
bool | {flag:bool} | true, false |
min(n) | {id:min(1)} | 1 and above |
max(n) | {id:max(100)} | 100 and below |
range(a,b) | {id:range(1,100)} | 1 to 100 |
length(n) | {code:length(5)} | exactly 5 chars |
Query String Parameters
Parameters not in the route template are automatically bound from the query string:
1// /Products/Search?q=laptop&minPrice=500&maxPrice=1500&sort=price
2public ActionResult Search(string q, decimal? minPrice, decimal? maxPrice, string sort = "relevance")
3{
4 var products = db.Products.Where(p => p.Name.Contains(q));
5
6 if (minPrice.HasValue)
7 products = products.Where(p => p.Price >= minPrice);
8 if (maxPrice.HasValue)
9 products = products.Where(p => p.Price <= maxPrice);
10
11 return View(products.ToList());
12}
Query strings are best for optional filters and search parameters where the URL should remain readable.
Mixing Route and Query Parameters
1// Route: /Products/Electronics?sort=price&page=2
2[Route("Products/{category}")]
3public ActionResult List(string category, string sort = "name", int page = 1)
4{
5 // category comes from the route
6 // sort and page come from the query string
7 return View();
8}
Model Binding with Complex Parameters
1public class ProductFilter
2{
3 public string Category { get; set; }
4 public decimal? MinPrice { get; set; }
5 public decimal? MaxPrice { get; set; }
6 public string Sort { get; set; } = "name";
7 public int Page { get; set; } = 1;
8}
9
10// /Products/Filter?Category=Electronics&MinPrice=100&Sort=price&Page=2
11public ActionResult Filter(ProductFilter filter)
12{
13 // All query parameters are bound to the model automatically
14 var products = db.Products
15 .Where(p => p.Category == filter.Category)
16 .OrderBy(filter.Sort)
17 .Skip((filter.Page - 1) * 20)
18 .Take(20);
19
20 return View(products.ToList());
21}
Generating URLs with Multiple Parameters
1// In Razor views
2@Url.Action("List", "Products", new { category = "Electronics", page = 2, pageSize = 50 })
3// /Products/Electronics/2/50 (if matching route exists)
4// /Products/List?category=Electronics&page=2&pageSize=50 (falls back to query string)
5
6@Html.ActionLink("Page 2", "List", "Products", new { category = "Electronics", page = 2 }, null)
Common Pitfalls
Route order matters: MVC matches the first route that fits the URL. If the default route {controller}/{action}/{id} comes before your custom route, it may capture the URL first. Always place more specific routes before generic ones.
Parameter name mismatch: The route template parameter name ({category}) must match the action method parameter name (string category). A mismatch causes the parameter to be null or trigger a 404.
Missing route constraints: Without constraints, /Products/Details/abc matches {id} even if the action expects an int, causing a runtime error. Add {id:int} constraints to reject non-matching URLs early with a 404.
Forgetting to enable attribute routing: [Route] attributes are ignored unless routes.MapMvcAttributeRoutes() is called in RouteConfig.cs. Without it, only convention-based routes work.
Ambiguous routes: Two routes that match the same URL pattern cause an AmbiguousMatchException. Use constraints or different URL structures to disambiguate.
Summary
The default route handles one parameter — add custom routes for multiple parameters
Attribute routing ([Route]) is more readable than centralized MapRoute definitions
Use route constraints ({id:int}, {name:alpha}) to validate parameters in the URL
Use query strings for optional filters and search parameters
Model binding automatically maps query string parameters to complex objects
Place specific routes before generic ones — MVC uses first-match ordering