skip to content

@RequestBody, @ModelAttribute & Data Binding

@RequestBody deserializes a JSON payload while @ModelAttribute binds form and query data through WebDataBinder, which you can constrain with @InitBinder and allowed-field lists. Interviewers raise the allow-list because unconstrained binding is a mass-assignment vulnerability.

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

questions

5

What is the difference between @RequestBody and @ModelAttribute in a Spring MVC controller?

level: juniorimportance: must knowfreq 78%

answer

  1. @RequestBody = body + HttpMessageConverter (Jackson)
  2. @ModelAttribute = params + WebDataBinder + setters
  3. InitBinder/allowedFields only touch ModelAttribute path
  4. Content-Type picks the converter
  5. JSON→RequestBody, HTML form→ModelAttribute

basics

~10 s

@RequestBody reads the raw request body (usually JSON) and deserializes it into an object using a message converter. @ModelAttribute binds request parameters (query string or HTML form fields) onto an object via its setters.

solid answer

~30 s

@RequestBody takes the whole HTTP request body and converts it into an object using an HttpMessageConverter chosen by the Content-Type header — typically MappingJackson2HttpMessageConverter for application/json. It ignores individual request parameters. @ModelAttribute works differently: it creates/obtains a target object and uses a WebDataBinder to bind name-matched request parameters (query params and application/x-www-form-urlencoded or multipart form fields) onto its properties through setters, applying type conversion. So @RequestBody is for JSON/XML API payloads and does NOT use WebDataBinder; @ModelAttribute is for classic HTML form submissions and DOES use WebDataBinder, which is what @InitBinder, allowed-field lists, PropertyEditors and the ConversionService customize.

code

java · 16 lines
java
@RestController
public class UserController {

    // JSON body -> Jackson deserialization, no WebDataBinder
    @PostMapping(value = "/api/users", consumes = "application/json")
    public User createJson(@RequestBody UserDto dto) {
        return service.create(dto);
    }

    // HTML form fields / query params -> WebDataBinder, setters
    @PostMapping(value = "/users", consumes = "application/x-www-form-urlencoded")
    public String createForm(@ModelAttribute UserForm form) {
        service.create(form);
        return "redirect:/users";
    }
}

go deeper

for a junior

Know the one-line distinction: body/JSON vs form/params, and that Content-Type matters.

for a middle

Explain that they use different resolvers (HttpMessageConverter vs WebDataBinder) and that customization hooks differ.

for a senior

Discuss error semantics (HttpMessageNotReadableException vs BindingResult) and why InitBinder/editors don't touch RequestBody.

for a principal

Weigh DTO design, over-posting risk on each path, and when to standardize on JSON vs form binding across an API surface.

## The two binding paths Spring MVC has **two completely separate mechanisms** for turning an HTTP request into a Java object, and knowing which one runs decides which customization hooks apply. ### @RequestBody — message conversion `@RequestBody User u` tells Spring to read the **entire raw HTTP request body** (the bytes after the headers) and deserialize it. Spring picks an `HttpMessageConverter` by matching the request's `Content-Type` header. For `application/json` that is normally `MappingJackson2HttpMessageConverter`, which delegates to a Jackson `ObjectMapper`. For XML it might be a Jackson XML or JAXB converter. Key points: - It reads the **body**, not query parameters or form fields. `?name=Bob` is invisible to `@RequestBody`. - The parsing rules come from **Jackson** (e.g. `@JsonProperty`, `@JsonIgnore`, `FAIL_ON_UNKNOWN_PROPERTIES`), **not** from `WebDataBinder`. - `@InitBinder`, `setAllowedFields`, `PropertyEditor`s do **NOT** affect `@RequestBody`. Those only touch the `WebDataBinder` path. - The body can be read only once; you generally have exactly one `@RequestBody` parameter per handler. ### @ModelAttribute — data binding via WebDataBinder `@ModelAttribute User u` (the annotation is optional for non-simple types — a bare `User u` parameter is treated as a model attribute) does this: 1. Obtains a target instance (new instance, or from the model / `@SessionAttributes`). 2. Creates a `WebDataBinder` around it. 3. Binds **request parameters** — the query string plus `application/x-www-form-urlencoded` or `multipart/form-data` form fields — onto the object by **matching parameter names to property names** and calling **setters**. 4. Converts each string value to the target property type using `PropertyEditor`s and the `ConversionService`. This is the mechanism customized by `@InitBinder`, `binder.setAllowedFields(...)`, `binder.registerCustomEditor(...)`, and registered `Converter`/`Formatter` beans. ### Content-Type decides which one you should use - Browser HTML form POST (`application/x-www-form-urlencoded`) or file upload (`multipart/form-data`) → `@ModelAttribute`. - JSON/XML REST payload (`application/json`) → `@RequestBody`. Sending JSON to a handler that expects `@ModelAttribute` silently binds nothing (no matching params); sending form fields to a `@RequestBody` handler yields a 415 or empty object. ### Gotcha: validation is separate Both support `@Valid`/`@Validated`, but *how* errors surface differs: a bind/conversion failure on `@ModelAttribute` populates `BindingResult` (recoverable if you add a `BindingResult` parameter); a malformed `@RequestBody` throws `HttpMessageNotReadableException` before any binding. (Validation triggering itself is covered by the Validation leaf.)

  • Does @InitBinder affect a @RequestBody parameter?
    No. @InitBinder configures the WebDataBinder, which is only used for @ModelAttribute / @RequestParam / @PathVariable binding. @RequestBody is handled by HttpMessageConverters (Jackson), so InitBinder, custom editors and allowed-field lists have no effect on it.
  • If a request sends both a JSON body and query parameters, can one handler read both?
    Yes: a handler can have a @RequestBody parameter (reads the body) and separate @RequestParam parameters (read the query string) at the same time — they use different resolvers. You just cannot bind the query params into the @RequestBody object.

saying these in an interview costs you the question

  • Claiming @RequestBody uses WebDataBinder or PropertyEditors
  • Thinking @ModelAttribute reads the JSON request body
  • Believing @InitBinder customizes @RequestBody deserialization
  • Saying both read from the same source (query params)

context

open as a page

What is the mass-assignment (over-posting) risk in Spring MVC data binding, and how do you defend against it?

level: seniorimportance: must knowfreq 55%

basics

~20 s

@ModelAttribute binds every request parameter whose name matches a property, so an attacker can add extra params (like admin=true) to set fields the form never exposed. Defend with setAllowedFields/setDisallowedFields in @InitBinder, or bind to a narrow DTO instead of the entity.

open as a page

What is WebDataBinder and what can you configure with an @InitBinder method?

level: middleimportance: should knowfreq 60%

basics

~20 s

WebDataBinder binds request parameters onto a target object and converts their types. An @InitBinder method receives that binder before binding so you can register custom editors/converters, restrict which fields may be bound, mark required fields, or attach a Validator.

open as a page

How do PropertyEditors and the ConversionService differ for converting request values, and which should you prefer?

level: seniorimportance: should knowfreq 48%

basics

~10 s

PropertyEditors are the old JavaBeans mechanism: stateful, not thread-safe, registered per binding, only String↔Object. ConversionService (Converter/Formatter) is the modern, stateless, thread-safe, type-to-any-type system registered globally. Prefer the ConversionService.

open as a page

Walk through how Spring resolves a @RequestBody parameter, including converter selection and failure modes.

level: principalimportance: should knowfreq 38%

basics

~20 s

A HandlerMethodArgumentResolver reads the request's InputStream, picks an HttpMessageConverter that supports the target type and the request's Content-Type, and uses it (e.g. Jackson) to deserialize the body. No match yields 415; a malformed body yields HttpMessageNotReadableException (400).

open as a page