Fading Coder

An Old Coder’s Final Dance

Home > Tech > Content

When to Use @RequestParam, @RequestBody, and @ModelAttribute in Spring MVC

Tech 1

This guide explains how Spring MVC binds incoming HTTP data to controller method parameters with @RequestParam, @RequestBody, and @ModelAttribute, when each is appropriate, and how they differ in behavior and data sources.

@RequestParam

Purpose

  • Read simple request parameters encoded as application/x-www-form-urlencoded
  • Works for query strings in GET and form fields in POST
  • Values ultimately come from HttpServletRequest#getParameter(...)

Example: GET request with query parameters

HTML (GET form)

<form action="/catalog/search" method="get">
  <div>
    <label>Keyword: <input type="text" name="q" /></label>
  </div>
  <div>
    <label>Page: <input type="number" name="page" /></label>
  </div>
  <button type="submit">Search</button>
</form>

Controller

@Controller
@RequestMapping("/catalog")
public class CatalogController {

  @GetMapping("/search")
  public String search(
      @RequestParam("q") String keyword,
      @RequestParam(name = "page", required = false, defaultValue = "1") int pageNo) {
    System.out.println("@RequestParam demo");
    System.out.println("q=" + keyword);
    System.out.println("page=" + pageNo);
    return "results";
  }
}

Equivalent approaches

  • Without @RequestParam when parameter names match request parameter names
@GetMapping("/search")
public String search(String q, Integer page) {
  System.out.println("q=" + q);
  System.out.println("page=" + page);
  return "results";
}
  • Using HttpServletRequest explicitly
@GetMapping("/search")
public String search(HttpServletRequest request) {
  String q = request.getParameter("q");
  String page = request.getParameter("page");
  System.out.println("q=" + q);
  System.out.println("page=" + page);
  return "results";
}

POST works the same way for application/x-www-form-urlencoded bodies

@PostMapping("/update-profile")
public String updateProfile(
    @RequestParam("displayName") String displayName,
    @RequestParam("email") String email) {
  // parameters come from form fields named displayName and email
  return "profile";
}

Notes

  • Spring converts Strings to target types using its ConversionService
  • @RequestParam targets key/value pairs in the parameter map; it does not read raw request bodies beyond what the servlet container exposes in getParameter(...)

@RequestBody

Purpose

  • Read the raw HTTP request body (HttpEntity) and convert it with HttpMessageConverters
  • Typically used with JSON, XML, or other non-form encodings (e.g., application/json)
  • Common with POST/PUT/PATCH. GET bodies are uncommon and usually unsupported by clients and servers

Example: Bind JSON to a POJO and return an object

@RestController
@RequestMapping("/books")
public class BookController {

  public static class TitleLookup {
    private String title;
    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }
  }

  public static class BookDto {
    private String title;
    private String author;
    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }
    public String getAuthor() { return author; }
    public void setAuthor(String author) { this.author = author; }
  }

  @PostMapping("/find-by-title")
  public BookDto findByTitle(@RequestBody TitleLookup payload) {
    System.out.println("payload: " + payload.getTitle());
    String name = payload.getTitle();
    // pretend to query a service
    BookDto result = new BookDto();
    result.setTitle(name);
    result.setAuthor("Unknown");
    return result;
  }
}

Client example (JSON POST)

fetch('/books/remove', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ nickname: 'alice', itemIds: '10,11,42' })
}).then(r => r.json());

Controller receiving a JSON object into a Map

@RestController
@RequestMapping("/books")
public class BookMutationController {

  private final BookService bookService;
  private final UserService userService;

  public BookMutationController(BookService bookService, UserService userService) {
    this.bookService = bookService;
    this.userService = userService;
  }

  @PostMapping("/remove")
  public void remove(@RequestBody Map<String, String> body) {
    String csvIds = body.get("itemIds");
    String nickname = body.get("nickname");
    String[] pieces = csvIds != null ? csvIds.split(",") : new String[0];
    Integer userId = userService.findUserIdByNickname(nickname);
    for (String p : pieces) {
      if (p == null || p.isBlank()) continue;
      int id = Integer.parseInt(p.trim());
      bookService.deleteForUser(id, userId);
    }
  }
}

Typed variant: Map to a DTO with a list

public static class RemoveRequest {
  private String nickname;
  private List<Integer> itemIds;
  public String getNickname() { return nickname; }
  public void setNickname(String nickname) { this.nickname = nickname; }
  public List<Integer> getItemIds() { return itemIds; }
  public void setItemIds(List<Integer> itemIds) { this.itemIds = itemIds; }
}

@PostMapping("/remove2")
public void remove2(@RequestBody RemoveRequest req) {
  Integer userId = userService.findUserIdByNickname(req.getNickname());
  for (Integer id : req.getItemIds()) {
    bookService.deleteForUser(id, userId);
  }
}

Notes

  • Requires Content-Type to match a configured HttpMessageConverter (e.g., application/json)
  • The body is parsed by the converter and bound to the method parameter
  • Not intended for form-encoded fields unless you deliberately send JSON; for stadnard forms perfer @RequestParam/@ModelAttribute

@ModelAttribute

Purpose

  • Bind form-encoded fields (application/x-www-form-urlencoded) to a Java object and place it in the model
  • Ideal for HTML form submissions to populate complex objects

HTML (POST form)

<form action="/users/register" method="post">
  <div>
    <label>User ID: <input type="number" name="id" /></label>
  </div>
  <div>
    <label>Username: <input type="text" name="username" /></label>
  </div>
  <div>
    <label>Password: <input type="password" name="password" /></label>
  </div>
  <button type="submit">Submit</button>
</form>

Model class

public class UserForm {
  private Integer id;
  private String username;
  private String password;
  public Integer getId() { return id; }
  public void setId(Integer id) { this.id = id; }
  public String getUsername() { return username; }
  public void setUsername(String username) { this.username = username; }
  public String getPassword() { return password; }
  public void setPassword(String password) { this.password = password; }
}

Controller

@Controller
@RequestMapping("/users")
public class UserController {

  @PostMapping("/register")
  public String register(@ModelAttribute UserForm form) {
    System.out.println("id=" + form.getId());
    System.out.println("username=" + form.getUsername());
    System.out.println("password=" + form.getPassword());
    return "ok";
  }
}

Notes

  • Field names in the form must match bean property names for proper binding
  • @ModelAttribute is also usable on method parameters without the annotation in many cases; Spring assumes complex types should be bound from the model/request parameters

Choosing Between Them

  • application/x-www-form-urlencoded
    • Single or small set of simple values: @RequestParam
    • Populate a object graph: @ModelAttribute
  • multipart/form-data (file uploads)
    • Use @RequestParam for simple fields, MultipartFile/@RequestPart for files; @RequestBody is not used for multipart
  • application/json, application/xml, or arbitrary body payloads
    • Use @RequestBody for binding the HTTP antity body

Additional details

  • @RequestParam uses the parameter map (query string + form fields) and Spring’s type conversion mechanism
  • @RequestBody uses HttpMessageConverters to deserialize the request body
  • @ModelAttribute uses data binding from request parameters to populate an object and adds it to the model
Tags: spring

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.