Path & Query Parameters
Most endpoints need to pull values out of the request: an id from the URL, filters from the query string, a token from a header. Spring MVC binds each of these declaratively with dedicated annotations. This page covers @PathVariable, @RequestParam, @RequestHeader, and @CookieValue, plus binding many params to a single object.
@PathVariable — values from the URL path
A @PathVariable binds a templated segment of the URL to a method parameter. The template name and the parameter name should match (or be named explicitly).
@GetMapping("/api/users/{id}")
public User byId(@PathVariable Long id) {
return service.findById(id);
}
// Multiple variables, explicit names
@GetMapping("/api/users/{userId}/orders/{orderId}")
public Order order(@PathVariable("userId") Long userId,
@PathVariable("orderId") Long orderId) {
return service.findOrder(userId, orderId);
}
Type conversion is automatic: {id} becomes a Long, a UUID, an enum, etc. A value that cannot convert produces 400 Bad Request.
Request:
curl http://localhost:8080/api/users/42
Output:
{ "id": 42, "name": "Ada Lovelace", "email": "[email protected]" }
@RequestParam — values from the query string
@RequestParam binds query-string parameters (and form fields). By default a request param is required.
@GetMapping("/api/products")
public List<Product> search(@RequestParam String category) { ... }
A missing required param returns 400 Bad Request. Make it optional with required = false or give it a defaultValue (which implies optional).
@GetMapping("/api/products")
public List<Product> search(
@RequestParam(required = false) String category,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
return service.search(category, page, size);
}
| Setting | Effect |
|---|---|
@RequestParam String q | required; 400 if missing |
@RequestParam(required = false) | optional; null if missing |
@RequestParam(defaultValue = "20") | optional; uses default if missing |
Optional<String> q | optional; empty if missing |
Tip: Prefer
defaultValueoverrequired = falseplus a null check — it removes a branch and documents the default in the signature.
Multiple values and maps
A repeated param binds to a List or array; an unknown set of params can bind to a Map.
// GET /api/products?tag=new&tag=sale
@GetMapping("/api/products")
public List<Product> byTags(@RequestParam List<String> tag) { ... }
// All query params at once
@GetMapping("/api/search")
public Results search(@RequestParam Map<String, String> filters) { ... }
Mapping many params to an object
When a handler has many filters, bind them to a POJO or record instead of a long parameter list. Spring populates fields from matching query params automatically — no annotation needed on the parameter.
public record ProductFilter(
String category,
BigDecimal minPrice,
BigDecimal maxPrice,
int page,
int size) {}
@GetMapping("/api/products")
public List<Product> search(ProductFilter filter) {
return service.search(filter);
}
Request:
curl "http://localhost:8080/api/products?category=books&minPrice=10&page=1&size=50"
Spring maps category, minPrice, page, and size onto the record components. Use @DateTimeFormat on fields when binding dates.
@RequestHeader — reading HTTP headers
@RequestHeader binds a header value. Like @RequestParam, it supports required and defaultValue.
@GetMapping("/api/profile")
public Profile profile(
@RequestHeader("Authorization") String authHeader,
@RequestHeader(value = "Accept-Language", defaultValue = "en") String lang) {
return service.profileFor(authHeader, lang);
}
// All headers in one shot
@GetMapping("/debug/headers")
public Map<String, String> headers(@RequestHeader Map<String, String> headers) {
return headers;
}
@CookieValue — reading cookies
@CookieValue binds a single cookie by name.
@GetMapping("/api/cart")
public Cart cart(@CookieValue(value = "SESSION", required = false) String sessionId) {
return service.cartFor(sessionId);
}
Putting it together
@RestController
@RequestMapping("/api/articles")
public class ArticleController {
private final ArticleService service;
public ArticleController(ArticleService service) {
this.service = service;
}
@GetMapping("/{id}")
public Article one(
@PathVariable Long id,
@RequestParam(defaultValue = "false") boolean includeComments,
@RequestHeader(value = "Accept-Language", defaultValue = "en") String lang) {
return service.find(id, includeComments, lang);
}
}
Request:
curl "http://localhost:8080/api/articles/7?includeComments=true" \
-H "Accept-Language: fr"
Output:
{
"id": 7,
"title": "Liaison des paramètres",
"language": "fr",
"comments": [ { "id": 1, "text": "Très utile !" } ]
}
Pitfalls
- An
int/longparam has no “absent” value — make it optional with adefaultValueor use a wrapper type /Optional. - Path variables containing special characters (slashes, dots) need care; use
{*var}to capture trailing slashes. - Header names are case-insensitive in HTTP, but match the conventional casing for readability.