Request Body Validation in Spring Boot
Fast recall
Quick revision
Core answer
@Valid @RequestBody on the controller parameter. Spring first converts JSON into that DTO and then checks its constraints. Use cascading @Valid for nested values, keep rules that depend on current data in the service, and keep final integrity rules in the database.Interview-ready guide
Interview answer
Spring first has to turn the request body into the controller's Java type. @RequestBody CreateOrderRequest request is read through an HTTP message converter. If the JSON is malformed or quantity cannot become the declared number type, conversion fails before the normal controller body receives a DTO. Bean Validation starts only after a Java object has been created.
The controller can declare @Valid @RequestBody CreateOrderRequest request. The DTO then carries local rules such as @NotBlank String productId, @Positive int quantity, or @NotEmpty List<...> items. In the normal Spring MVC flow, a failed request-body validation becomes a bad-request response unless the application handles the validation result in another supported way.
Putting constraints on a child DTO is not enough; validation must be told to enter that child. For a list of order items, List<@Valid OrderItemRequest> items tells Bean Validation to inspect each element, where rules such as @NotBlank and @Positive live. @Valid is a cascade marker; it is not itself a rule such as non-null or positive.
A valid DTO can still describe an order the system must reject. Product existence, available stock, coupon eligibility, and authorization depend on current application state, so the service checks them. A database uniqueness or foreign-key constraint remains the final protection against races and invalid stored data. An annotation alone cannot safely replace those checks.
A central @ControllerAdvice can turn conversion and validation exceptions into one stable error format with a field path, rule code, and readable message. It should not expose parser internals or stack traces. Tests should cover malformed JSON, a missing required value, an invalid nested item, one valid request, and a structurally valid request that the service rejects.
Request Body Validation in Spring Boot
Direct Answer
Put Jakarta Bean Validation constraints on a request DTO and use @Valid @RequestBody on the controller parameter. Spring first converts JSON into that DTO and then checks its constraints. Use cascading @Valid for nested values, keep rules that depend on current data in the service, and keep final integrity rules in the database.
Deep Dive & Architecture
Four different failures can look like “bad input.” The JSON may be malformed, mapping may fail because a value cannot become the declared Java type, Bean Validation may reject the constructed DTO, or domain logic may reject a structurally valid command. Keeping these stages separate produces clearer code and more accurate errors.
Bean Validation is strongest for local, deterministic constraints: nullability, string form, numeric ranges, collection size, and relationships among fields in one object through a custom class-level constraint. Cascading is explicit. @Valid by itself is not a constraint; it tells the validator to inspect nested constrained objects.
Rules that query changing state do not fit a simple field annotation. A uniqueness check in validation can pass and still race with another insert, so the database also needs a unique constraint. The service catches that conflict and maps it to the API outcome. Authorization likewise should not be disguised as validation.
Error responses should let clients attach a failure to a field without parsing prose. A stable problem type plus an extension array of field path and rule code works well. The human message can be localized. Reject unexpected data according to the service's compatibility policy and cap body and collection sizes before expensive processing.
Code Example
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
record CreateOrderRequest(
@NotEmpty List<@Valid OrderItemRequest> items) {}
record OrderItemRequest(
@NotBlank String productId,
@Positive int quantity) {}
@RestController
@RequestMapping("/orders")
final class OrderController {
private final OrderService orderService;
OrderController(OrderService orderService) { this.orderService = orderService; }
@PostMapping
ResponseEntity<OrderResponse> create(
@Valid @RequestBody CreateOrderRequest request) {
return ResponseEntity.status(201).body(orderService.create(request));
}
}
interface OrderService { OrderResponse create(CreateOrderRequest request); }
record OrderResponse(long id) {}
@Valid on each collection element cascades to the item record. Product existence and stock availability still belong in orderService, backed by database integrity rules.
Check your understanding
Answer these without looking back at the article.
- What is the difference between JSON conversion failure and Bean Validation failure?
- Why is `@Valid` needed on nested request objects?
- Why should a uniqueness rule also exist in the database?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.