Route Parameters
🎯 What You'll Learn
- Required route parameters
- Optional route parameters
- Default values
- Catch-all parameters
- Complex route patterns
- Parameter binding
What are Route Parameters?
Route parameters capture values from the URL and pass them to your action methods.
Route: /api/products/{id}
URL: /api/products/123
↓
Parameter: id = 123
Required Parameters
Required parameters must be present in the URL.
[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
return Ok($"Product {id}");
}
// ✅ /api/products/123 → id = 123
// ❌ /api/products → 404 Not Found
Multiple Required Parameters
[HttpGet("{category}/{id}")]
public IActionResult GetProduct(string category, int id)
{
return Ok($"Category: {category}, Product: {id}");
}
// /api/products/electronics/123
// → category = "electronics", id = 123
Optional Parameters
Optional parameters use ? suffix and can be omitted.
[HttpGet("{id?}")]
public IActionResult GetProducts(int? id)
{
if (id.HasValue)
return Ok($"Product {id}");
return Ok("All products");
}
// ✅ /api/products → id = null
// ✅ /api/products/123 → id = 123
Optional route parameters should use nullable types (int?, string?)
in the action method.
Default Values
Default values are used when the parameter is not provided.
[HttpGet("{page=1}")]
public IActionResult GetProducts(int page)
{
return Ok($"Page {page}");
}
// /api/products → page = 1 (default)
// /api/products/5 → page = 5
Conventional Routing Default
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
// / → Home/Index
// /Products → Products/Index
// /Products/Details/123 → Products/Details/123
Catch-All Parameters
Catch-all parameters capture the rest of the URL path.
[HttpGet("{*path}")]
public IActionResult CatchAll(string path)
{
return Ok($"Path: {path}");
}
// /api/files/docs/2024/report.pdf
// → path = "docs/2024/report.pdf"
Practical Use Case: File Serving
[Route("api/files")]
public class FilesController : ControllerBase
{
[HttpGet("{*filePath}")]
public IActionResult GetFile(string filePath)
{
var fullPath = Path.Combine("uploads", filePath);
if (!System.IO.File.Exists(fullPath))
return NotFound();
return PhysicalFile(fullPath, "application/octet-stream");
}
}
Complex Route Patterns
Blog Post Example
[Route("blog")]
public class BlogController : Controller
{
// /blog/2024/12/10/my-post-title
[HttpGet("{year}/{month}/{day}/{slug}")]
public IActionResult Post(
int year,
int month,
int day,
string slug)
{
return Ok($"Post: {year}-{month}-{day} - {slug}");
}
}
Product Search Example
[Route("api/products")]
public class ProductsController : ControllerBase
{
// /api/products/category/electronics/price/100-500
[HttpGet("category/{category}/price/{minPrice}-{maxPrice}")]
public IActionResult SearchByPriceRange(
string category,
decimal minPrice,
decimal maxPrice)
{
return Ok($"{category}: ${minPrice}-${maxPrice}");
}
}
Parameter Binding
ASP.NET Core automatically binds route parameters to action method parameters.
Automatic Type Conversion
[HttpGet("{id}/{active}")]
public IActionResult GetProduct(int id, bool active)
{
// /api/products/123/true
// → id = 123 (int), active = true (bool)
return Ok();
}
Custom Model Binding
public record DateRange(int Year, int Month, int Day);
[HttpGet("{year}/{month}/{day}")]
public IActionResult GetByDate([FromRoute] DateRange date)
{
// /api/products/2024/12/10
// → date.Year = 2024, date.Month = 12, date.Day = 10
return Ok(date);
}
Complete InvenTrack Example
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
// Required parameter
// GET /api/products/123
[HttpGet("{id:int}")]
public IActionResult GetById(int id)
{
return Ok($"Product {id}");
}
// Optional parameter
// GET /api/products/page or /api/products/page/2
[HttpGet("page/{pageNumber?}")]
public IActionResult GetPage(int? pageNumber)
{
var page = pageNumber ?? 1;
return Ok($"Page {page}");
}
// Default value
// GET /api/products/list or /api/products/list/10
[HttpGet("list/{pageSize=20}")]
public IActionResult GetList(int pageSize)
{
return Ok($"Page size: {pageSize}");
}
// Multiple parameters
// GET /api/products/category/electronics/123
[HttpGet("category/{category}/{id}")]
public IActionResult GetByCategory(string category, int id)
{
return Ok($"{category}: Product {id}");
}
// Complex pattern
// GET /api/products/price/100-500
[HttpGet("price/{minPrice:decimal}-{maxPrice:decimal}")]
public IActionResult GetByPriceRange(decimal minPrice, decimal maxPrice)
{
return Ok($"Price range: ${minPrice} - ${maxPrice}");
}
// Catch-all for file paths
// GET /api/products/images/2024/product-123.jpg
[HttpGet("images/{*imagePath}")]
public IActionResult GetImage(string imagePath)
{
return Ok($"Image path: {imagePath}");
}
}
Key Takeaways
- Required parameters:
{id}- must be present - Optional parameters:
{id?}- can be omitted - Default values:
{page=1}- fallback value - Catch-all:
{*path}- captures rest of URL - Multiple parameters:
{category}/{id} - Complex patterns:
{min}-{max} - Automatic binding: Route values → action parameters
- Type conversion: Strings → int, bool, decimal, etc.
- Nullable types: Use for optional parameters
You now understand route parameters! In the next section, we'll explore Route Constraints—how to restrict parameter values and ensure only valid URLs match your routes.