Route Values and the Query String
Values reach an action from the route (/products/7) or the query string (?page=2). Choose which with the address's meaning, give optional values defaults, and use [FromRoute] and [FromQuery] to be explicit.
Journeyman · ASP.NET Core · C# · Updated
After this lesson you can
- Read values from the route and the query string, with defaults for optional ones
- Choose between a route value and a query string value for a piece of information
Key takeaways
- A route value such as
{id:int}fills the parameter with the same name and is required unless marked with?; other parameters come from the query string. - Give a query string parameter a default, such as
int page = 1, and it's optional. [FromQuery(Name = "q")]reads?q=sauceinto a parameter with a clearer C# name;[FromRoute]and[FromForm]name the other sources.- If leaving a value out makes no sense, it belongs in the route (
/products/7); if it means "the default", it belongs in the query string (?page=2).
An action's parameters are filled from the request. Two parts of the address carry values:
/products/7 the route: id = 7
/products?page=2 the query string: page = 2
/products/search?q=tofu the query string: q = tofu
From the route
A value in the route template, such as {id:int}, fills the parameter with the same name:
[HttpGet("{id:int}")]
public IActionResult Details(int id)
/products/7 calls Details(7). The route can't match without it, so a route value is required (unless it's marked optional with ?).
From the query string
Any other parameter is looked for in the query string, the part after ?. Give it a default value, and it's optional:
[HttpGet("")]
public IActionResult Index(int page = 1)
{
int pageCount = (productDao.Count() + PageSize - 1) / PageSize;
if (page < 1 || page > pageCount)
{
return NotFound();
}
ViewData["Page"] = page;
ViewData["PageCount"] = pageCount;
return View(productDao.GetPage(page, PageSize));
}
| Address | page |
|---|---|
/products |
1, the default |
/products?page=2 |
2 |
/products?page=8 |
8, which is past the last of 7 pages, so Not Found |
/products?page=abc |
1: abc isn't an int, so the default stays, and ModelState records the problem |
Tag helpers write query strings for you: asp-action="Index" asp-route-page="2" gives href="/products?page=2", because page isn't part of the route.
Saying where a value comes from
Usually the name is enough. When you want to be explicit, or the name in the address differs from the parameter's, use [FromRoute] and [FromQuery]:
[HttpGet("search")]
public IActionResult Search([FromQuery(Name = "q")] string? text)
Now /products/search?q=sauce fills text with sauce. A short q is the usual name for a search box; the parameter keeps its clear C# name. And ?text=sauce fills nothing: with [FromQuery(Name = "q")], only q counts.
| Attribute | Takes the value from |
|---|---|
[FromRoute] |
The route template, such as {id} |
[FromQuery] |
The query string |
[FromForm] |
A posted form's fields |
(none) |
Wherever binding finds the name: form, route, then query string |
Route or query string?
| Use the route for | Use the query string for |
|---|---|
Which thing the page is about: /products/7, /customers/ALFKI |
How to show it: ?page=2, ?sort=price, ?q=sauce |
| Values the page can't do without | Optional values, with defaults |
| Addresses people bookmark and share as "the page for X" | Filters and settings that change the same page's view |
A rule of thumb: if leaving the value out makes no sense, it belongs in the route; if leaving it out means "the default", it belongs in the query string.
Check yourself
2 questions to try on the lesson page.
So what
The route says what the page is about; the query string says how to show it. Choose by meaning, give optional values defaults, and your addresses explain themselves.
Key terms
- Route Parameter
- Query String
- [FromQuery]
- [FromRoute]