Request Bodies & @RequestBody
Request Bodies & @RequestBody
When a client sends data to your API — creating a new user, updating an order, submitting a form — that data travels inside the HTTP request body, typically as JSON. Spring Boot's @RequestBody annotation tells the framework to deserialise that JSON into a Java object automatically. Understanding how that process works, and why you should bind into a Data Transfer Object (DTO) rather than directly into a JPA entity, is one of the most important design decisions you will make in a REST API.
How @RequestBody Works
When a request arrives at a controller method annotated with @RequestBody, Spring's DispatcherServlet delegates to an HttpMessageConverter. Because Spring Boot auto-configures Jackson (the MappingJackson2HttpMessageConverter), every controller method that accepts a Content-Type: application/json request gets automatic JSON-to-Java deserialization for free.
Spring instantiates the target class, matches each JSON field name to a Java field or setter by name, and fills it in. Fields not present in the JSON are left at their default values (usually null or zero).
null. This is a very common beginner bug.
What Is a DTO and Why Does It Matter?
A Data Transfer Object is a plain Java class whose sole purpose is to carry data across a boundary — in this case, between the HTTP wire and your service layer. It has no persistence annotations, no business logic, and no framework dependencies. Compare these two approaches:
Approach A — binding directly into a JPA entity (avoid this):
Approach B — binding into a dedicated DTO (correct approach):
Approach A suffers from mass assignment — a malicious client can set fields like id, role, or isAdmin just by including them in the JSON body. Spring will happily bind them unless you explicitly block every field. Approach B is safe by default: the client can only supply the fields you deliberately put on the DTO.
Defining a DTO Class
A DTO is an ordinary POJO. For a create-user request it might look like this:
Notice the jakarta.validation.constraints.* annotations. They do nothing by themselves — you must activate them with @Valid in the controller.
Validating the Request Body with @Valid
Add @Valid (from jakarta.validation) immediately before the @RequestBody parameter. Spring Boot will run Bean Validation on the DTO before the method body executes. If any constraint fails, Spring returns a 400 Bad Request automatically.
@Valid (Jakarta EE) triggers standard Bean Validation on the method parameter. @Validated (Spring) additionally supports validation groups. For most REST endpoints @Valid is the right choice — keep it simple.
Using Java Records as DTOs (Spring Boot 3+)
Java records are immutable, concise, and ideal for DTOs. Jackson 2.12+ deserializes them out of the box — no special configuration required in Spring Boot 3:
The record replaces the boilerplate getters, constructor, and equals/hashCode entirely. Use records for request DTOs; they enforce immutability so nothing can accidentally mutate the incoming data.
Separating Request and Response DTOs
A common pattern is to use separate classes for inbound and outbound data:
- Request DTO (
UserCreateRequest) — fields the client is allowed to send. Carries validation annotations. - Response DTO (
UserResponse) — fields the client is allowed to see. Omits sensitive data like passwords or internal flags.
The service layer creates the entity from the request DTO, saves it, and returns a response DTO built from the saved entity. The client never sees the entity class at all.
Handling Nested and List Bodies
Jackson handles nested objects and lists automatically. If your JSON contains an array at the top level, declare the parameter as a List<YourDto>:
@Valid on the List parameter alone does NOT cascade into the list elements in some Spring configurations. Annotate the generic type parameter (List<@Valid UserCreateRequest>) or validate manually in the service layer.
Summary
@RequestBody tells Spring to deserialize the JSON body into a Java object using Jackson. Always bind into a dedicated DTO — never a JPA entity — to prevent mass assignment attacks and to decouple your API surface from your database schema. Add @Valid to activate Bean Validation before the method runs. Java records are the most concise and safe DTO form in Spring Boot 3. Use separate request and response DTOs to control exactly what the client can send and what it receives back. This pattern is the foundation every professional Spring Boot API is built on.