HuntExams Academy logo
JavaIntermediate#spring

What is the difference between @RequestParam, @PathVariable, and @RequestBody?

Answer

All three bind part of an HTTP request to a controller method argument in Spring MVC, but they read different parts of it. @RequestParam reads query-string parameters such as ?page=2&size=20, and form fields for application/x-www-form-urlencoded submissions. Use it for optional filtering, sorting and paging values. It supports required=false, defaultValue, Map<String,String> for every parameter, and List<String> for a repeated parameter. A missing required parameter produces 400 Bad Request. @PathVariable reads a templated segment of the URI itself, as in /users/{id}, so it identifies WHICH resource you are addressing. It should be mandatory and part of the resource identity; a mismatch results in 404 or 400. Use required=false only with an Optional argument type. @RequestBody deserialises the whole request body \u2014 usually JSON \u2014 into an object through an HttpMessageConverter, Jackson by default. Use it for POST, PUT and PATCH payloads with several fields or nested structures. Combine it with @Valid to trigger Bean Validation; violations raise MethodArgumentNotValidException and map to 400. Only one @RequestBody per method, and GET requests should not carry one. REST convention: identity in the path, filters and pagination in the query string, data in the body. So PUT /users/42?notify=true with a JSON body legitimately uses all three at once. Related annotations often asked alongside: @RequestHeader, @CookieValue, @ModelAttribute (binds query or form parameters onto an object), @RequestPart for multipart uploads, and @ResponseBody / @RestController for the response side. Note that on Spring Boot 3 you should compile with -parameters or name the parameters explicitly, since parameter names are otherwise not retained.

Example

@RestController
@RequestMapping("/api/users")
class UserController {

  // GET /api/users?role=admin&page=2
  @GetMapping
  List<User> list(
      @RequestParam(required = false) String role,
      @RequestParam(defaultValue = "0")  int page,
      @RequestParam(defaultValue = "20") int size) {
    return service.find(role, page, size);
  }

  // GET /api/users/42
  @GetMapping("/{id}")
  User byId(@PathVariable Long id) {
    return service.get(id);
  }

  // POST /api/users  with a JSON body
  @PostMapping
  @ResponseStatus(HttpStatus.CREATED)
  User create(@Valid @RequestBody CreateUserRequest req) {
    return service.create(req);
  }

  // All three together:
  // PUT /api/users/42?notify=true   body: {"name":"Ravi"}
  @PutMapping("/{id}")
  User update(@PathVariable Long id,
              @RequestParam(defaultValue = "false") boolean notify,
              @Valid @RequestBody UpdateUserRequest req) {
    return service.update(id, req, notify);
  }
}

record CreateUserRequest(@NotBlank String name, @Email String email) {}

Detailed explanation

In Spring MVC, @RequestParam, @PathVariable and @RequestBody bind different parts of an HTTP request to controller method arguments. @RequestParam reads request parameters, typically query-string values or URL-encoded form fields; @PathVariable extracts values captured by URI templates; @RequestBody reads and converts the request body. The REST convention is resource identity in the path, filtering and pagination in query parameters, and structured input in the body. For example, PUT /users/42?notify=true with a JSON payload can legitimately use all three annotations together.

@RequestParam("page") int page reads a parameter such as ?page=2. Parameters are required by default: a missing required value normally produces 400 Bad Request. Use required=false for optional parameters, choosing a nullable wrapper or Optional when appropriate. A defaultValue supplies a fallback for missing or empty values and implicitly makes the parameter optional. List<String> supports repeated values such as ?tag=java&tag=spring; an unnamed Map<String,String> collects parameters, while MultiValueMap preserves repeated values. Spring's conversion system converts strings into declared types; invalid numeric input normally produces 400.

@PathVariable("id") Long id reads 42 from a matching route such as /users/{id}. Route matching happens before argument binding: a URL that does not match any applicable mapping normally produces 404, whereas /users/abc matching this template produces 400 when conversion to Long fails. Path variables are required by default, but required=false can work with a nullable reference type; Optional is another option. Supporting an absent segment generally also requires a mapping without that segment. Explicit annotation names avoid dependence on compiler-retained parameter names.

@RequestBody delegates body conversion to an HttpMessageConverter selected using the target type and request Content-Type; Jackson commonly handles JSON when configured. It suits structured POST, PUT and PATCH payloads, including nested objects. With a validation provider configured, @Valid triggers Bean Validation; DTO violations normally raise MethodArgumentNotValidException and return 400. Malformed JSON normally returns 400, while unsupported media types return 415. Use one body argument because the request body is a single stream, and avoid GET bodies because they lack generally defined semantics. Interviewers look for correct source selection, conversion behaviour and error handling—not just annotation definitions.

Key points to remember

  • @RequestParam binds query or form parameters and supports optional values, defaults and repeated parameters.
  • @PathVariable binds URI-template values, commonly resource identifiers.
  • @RequestBody converts the body into an object using an HttpMessageConverter.
  • String parameter conversion and JSON deserialisation are different mechanisms; @Valid adds validation after binding.
  • A controller method can combine all three, for example an update with an ID, a notification flag and a JSON DTO.

Common mistakes candidates make

  • Claiming @RequestParam reads only query strings, or that it extracts fields from a JSON body.
  • Saying every path-variable problem returns 404: a matched route with an invalid numeric ID normally returns 400.
  • Assuming @RequestBody automatically validates constraints or that required=false requires an Optional path-variable argument.

Likely follow-up questions

How is @ModelAttribute different from @RequestBody?

@ModelAttribute creates or obtains an object and binds request parameters onto its properties, commonly for forms. @RequestBody instead uses an HttpMessageConverter to deserialize the body, commonly JSON.

How do you handle optional inputs safely?

For @RequestParam, use a defaultValue, Optional<T>, or required=false with a nullable reference type. An optional path variable also needs a route that matches without the segment; required=false alone does not change route matching.

Should parameter names be explicitly included in annotations?

Using @RequestParam("page") and @PathVariable("id") avoids reliance on Java parameter-name discovery. If you omit these names, compile with -parameters, particularly with modern Spring versions.

Interview tip: Start with “query or form, URI template, and body”, then demonstrate all three using one update request and explain the main error cases.

Related Interview Questions

1
JavaIntermediate#spring

What are Spring bean scopes?

Open
2
JavaIntermediate#puzzle#operators

What does this print? int x = 5; System.out.println(x++ + ++x);

Open
3
JavaBeginner#puzzle#strings

What is the output of this code? String s1 = "abc"; String s2 = "abc"; String s3 = new String("abc"); System.out.println(s1 == s2); System.out.println(s1 == s3);

Open