skip to content

@RequestParam / @PathVariable / @RequestHeader

Binding query parameters, path variables, headers and cookies into method arguments, with required flags, defaults, Optional and automatic type conversion. Basic but constantly asked, usually as 'what happens when the parameter is missing'.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What is the difference between @RequestParam and @PathVariable, and when do you use each?

level: juniorimportance: must knowfreq 85%

answer

  1. path template {id} vs query ?id=
  2. PathVariable = resource identity; RequestParam = filter/sort/page
  3. PathVariable required (else 404); RequestParam required-by-default (else 400)
  4. both auto-convert String -> target type
  5. explicit name needed without -parameters

basics

~20 s

@PathVariable pulls a value out of the URL path itself (e.g. /users/42 -> id=42). @RequestParam reads a query-string parameter after the ? (e.g. /users?id=42) or a form field. You use @PathVariable to identify a resource, @RequestParam to filter/paginate/search.

solid answer

~30 s

@PathVariable binds a segment of the URI path declared in the mapping template, like @GetMapping("/users/{id}") -> @PathVariable Long id — it identifies a specific resource. @RequestParam binds a query-string parameter (?page=2) or, for form POSTs, an application/x-www-form-urlencoded body field. Both do automatic type conversion (String -> Long, enum, etc.). @RequestParam supports required, defaultValue, and Optional; @PathVariable is required by default because a missing segment means the URL simply doesn't match the mapping. Rule of thumb: path variables for hierarchical resource identity (REST style), request params for optional modifiers like filtering, sorting, and pagination.

code

java · 17 lines
java
@RestController
@RequestMapping("/users")
class UserController {

    // GET /users/42  -> id = 42 (path identifies the resource)
    @GetMapping("/{id}")
    public User byId(@PathVariable Long id) {
        return service.find(id);
    }

    // GET /users?status=ACTIVE&page=2  -> query params filter/paginate
    @GetMapping
    public List<User> list(@RequestParam(required = false) Status status,
                           @RequestParam(defaultValue = "0") int page) {
        return service.search(status, page);
    }
}

go deeper

for a junior

Must nail the path-vs-query distinction and give a correct URL example for each.

for a middle

Should mention required-by-default behavior and type conversion for both.

for a senior

Should articulate the REST convention (identity vs modifier) and the 404-vs-400 distinction.

for a principal

Frames it as URI design / API contract and knows the argument-resolver machinery underneath.

Spring MVC controllers are classes annotated with @Controller or @RestController whose handler methods are mapped to HTTP requests via @RequestMapping (or shortcuts @GetMapping, @PostMapping, etc.). Method parameters are populated by *argument resolvers*; @RequestParam, @PathVariable, @RequestHeader, and @CookieValue are the annotations that tell Spring where to pull a simple (scalar) value from. **@PathVariable** extracts a variable embedded in the URI *path template*. You declare a placeholder in the mapping with curly braces and bind it: ```java @GetMapping("/users/{id}") public User get(@PathVariable Long id) { ... } ``` For GET /users/42, `id` becomes the Long 42. The name is matched by parameter name; if the code isn't compiled with parameter names you must be explicit: @PathVariable("id"). Path variables are *required by definition* — if the segment is absent the URL doesn't match the template at all, so you'd get a 404, not a missing-value error. You can capture multiple variables (/orders/{orderId}/items/{itemId}) and even bind them all at once into a Map<String,String> with @PathVariable Map<String,String> vars. **@RequestParam** binds a *query-string* parameter (the part after ?, e.g. /search?q=spring&page=2) or a field from an application/x-www-form-urlencoded request body (a classic HTML form POST). It also binds multipart file parts (MultipartFile). Example: ```java @GetMapping("/search") public Page<Item> search(@RequestParam String q, @RequestParam(defaultValue = "0") int page) { ... } ``` @RequestParam is *required by default*; a missing param throws MissingServletRequestParameterException -> HTTP 400. You relax that with required = false, defaultValue = "..." (which also implies not-required), or by declaring the type as Optional<T> or a nullable type. **Type conversion**: both annotations run the incoming String through Spring's conversion machinery (WebConversionService / ConversionService), so String, primitives and wrappers, enums, and types with a registered Converter/Formatter (e.g. LocalDate via the DateTimeFormat support) all bind automatically. A value that can't be converted (e.g. ?page=abc for an int) yields a 400 (MethodArgumentTypeMismatchException). **When to use which**: follow REST conventions. Use @PathVariable for the *identity/hierarchy* of the resource you're acting on — /users/{id}, /users/{id}/orders/{orderId}. Use @RequestParam for *optional modifiers* that don't identify the resource — filtering (?status=ACTIVE), searching (?q=...), sorting (?sort=name), and pagination (?page=&size=). A useful test: if the value names *which thing*, it's a path variable; if it *narrows or shapes the response*, it's a request param. **Common gotchas**: (1) Forgetting to compile with -parameters (Kotlin/Java) so implicit name binding fails — Spring Boot enables this by default, but plain setups may need it. (2) Using @RequestParam to read a JSON body — it won't; JSON goes to @RequestBody. (3) Trailing/encoding surprises: a path variable stops at the next / by default, so an id containing a slash needs a regex in the template like {path:.+}.

  • If GET /users/42 returns 404, name two likely causes related to @PathVariable.
    Either the mapping template doesn't declare {id} (or the name doesn't match the parameter and no explicit @PathVariable("id") is given), or the id contains a '/' that the default single-segment matcher won't capture without a regex like {id:.+}. A conversion failure would be 400, not 404.
  • Can @RequestParam read a value from a POST body?
    Yes, if the body is application/x-www-form-urlencoded (a classic HTML form) or multipart/form-data — @RequestParam binds those fields, and MultipartFile for file parts. It does NOT read JSON bodies; those require @RequestBody.

saying these in an interview costs you the question

  • Claiming @PathVariable reads the query string (it reads the path template).
  • Saying @RequestParam is not required by default (it IS required unless you set required=false, defaultValue, or Optional).
  • Thinking @RequestParam can bind a JSON request body.

context

open as a page

How do you make a @RequestParam optional? Explain required, defaultValue, and Optional, and how they interact.

level: middleimportance: must knowfreq 75%

basics

~20 s

By default @RequestParam is required, so a missing param -> 400. Make it optional with required=false (value is null when absent), or give defaultValue="..." (used when absent, and implies not-required), or declare the type as Optional<T> or a nullable type.

open as a page

How do you bind multi-valued or dynamic query parameters — repeated params, a Map of all params, and MultiValueMap?

level: seniorimportance: should knowfreq 45%

basics

~10 s

For a repeated param (?tag=a&tag=b) bind List<String> or String[]. To grab every query param at once, use @RequestParam Map<String,String> (one value per name) or @RequestParam MultiValueMap<String,String> (preserves all values per name).

open as a page

What actually converts a @RequestParam/@PathVariable String into a LocalDate, enum, or custom value object — and how do you customize or fix a conversion failure?

level: principalimportance: should knowfreq 35%

basics

~20 s

Spring runs the raw String through a ConversionService (a FormattingConversionService) using registered Converter and Formatter implementations. For dates you add @DateTimeFormat or a Formatter; for custom types you register a Converter<String, YourType>. Failures throw MethodArgumentTypeMismatchException -> 400.

open as a page