How do you validate incoming request data in Spring Boot (Bean Validation, @Valid)?

8 minintermediatevalidationbean-validationrequestbody

Quick Answer

Spring Boot integrates the Bean Validation standard (Jakarta Validation, implemented by Hibernate Validator) via annotations like @NotNull, @NotBlank, @Size, and @Email placed directly on a request DTO's fields. Adding @Valid (or @Validated) before a @RequestBody parameter in a controller method tells Spring MVC to run those validation rules automatically before the handler executes; if validation fails, Spring throws a MethodArgumentNotValidException, which you typically handle globally via @ExceptionHandler/@ControllerAdvice to return a structured 400 Bad Request response.

Detailed Answer

Spring Boot integrates the Bean Validation standard (Jakarta Validation API, typically implemented by Hibernate Validator, pulled in automatically by spring-boot-starter-validation) for declarative, annotation-based input validation:

1. Annotate the request DTO's fields:

record CreateOrderRequest(
    @NotBlank String customerId,
    @Min(1) int quantity,
    @Email String contactEmail
) { }

2. Add @Valid in the controller to trigger validation on that parameter:

@RestController
class OrderController {
    @PostMapping("/orders")
    Order createOrder(@Valid @RequestBody CreateOrderRequest request) {
        // if we reach this line, request has already passed all validation rules
    }
}

If validation fails, Spring MVC throws MethodArgumentNotValidException before the handler method body ever executes — the controller code never has to manually check if (quantity < 1) ....

3. Handle validation failures globally, converting them into a consistent, structured error response instead of a generic 500:

@RestControllerAdvice
class ValidationExceptionHandler {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    ResponseEntity<Map<String, String>> handleValidation(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getFieldErrors()
            .forEach(err -> errors.put(err.getField(), err.getDefaultMessage()));
        return ResponseEntity.badRequest().body(errors); // 400, with field-level error details
    }
}

Common annotations: @NotNull/@NotBlank/@NotEmpty (subtly different: @NotNull allows empty strings, @NotBlank doesn't allow blank/whitespace-only strings, @NotEmpty disallows empty-but-not-null collections/strings), @Size(min=, max=), @Min/@Max, @Email, @Pattern(regexp=).

Nested object validation: if a DTO contains another validated object as a field, add @Valid on that nested field too, or the nested object's own constraints won't be checked — @Valid on the controller parameter alone only validates that top-level object's direct fields plus explicitly @Valid-annotated nested fields.

@Validated vs @Valid: @Valid is the standard JSR-380 annotation; @Validated is a Spring-specific variant that additionally supports validation groups and enables method-level validation (validating parameters of a plain @Service method, not just @RequestBody controller arguments).

Related Resources