Java

Chapter 126: Request Validation in Java — Jakarta Validation, @Valid, and Consistent 400 Responses

Validate HTTP requests in Spring Boot with jakarta.validation constraints on DTOs, @Valid on controller parameters, MethodArgumentNotValidException handling, and custom validators when needed.

Author: Sushil Kumar

Java Spring validationJava Valid RequestBody SpringJakarta validation annotationsJava MethodArgumentNotValidExceptionSpring Boot request validation

Chapter 126: Request Validation in Java

Request validation rejects bad input early—before services or databases waste work. In Spring Boot, spring-boot-starter-validation brings Jakarta Bean Validation annotations (@NotBlank, @Size, @Email, @Positive) you place on DTO fields or controller parameters, then trigger with @Valid.


1. Topic title

Validation: declarative constraints on DTOs; framework binds errors to HTTP 400


2. What it means

public record CreateItemRequest(@NotBlank String name, @Size(max = 500) String note) { }

Controller signatureResponseEntity<?> create(@Valid @RequestBody CreateItemRequest body)@Valid cascades validation; failures become MethodArgumentNotValidException (or ConstraintViolationException for param validation).

Custom rules@Constraint + ConstraintValidator when cross-field logic exceeds built-ins.


3. Why it is used

Uniform 400 bodies instead of hand-rolled if (name == null) in every method.

Documentation—annotations double as API schema hints when you export OpenAPI.


4. Mental sketch

Validation is the bouncer at the club doorID format, dress code, capacitybefore you reach the bar (service layer) where real money moves.


5. Java code example

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
 
public record CreateItemRequest(@NotBlank String name, @Size(max = 500) String note) {
}
 
@RestController
class ItemsController {
    private final ItemService items;
 
    ItemsController(ItemService items) {
        this.items = items;
    }
 
    @PostMapping("/api/items")
    ResponseEntity<ItemResponse> create(@Valid @RequestBody CreateItemRequest body) {
        return ResponseEntity.status(201).body(items.createFromRequest(body));
    }
}

ItemService.createFromRequest maps DTO → entity → persisted row → response DTO; validation covers shape, while rules like unique name still live in the service (409 path—Chapter 127 maps exceptions to HTTP).


6. Explanation of code

@Valid on nested DTOs validates whole trees—handy for address objects inside requests.


7. Common mistakes

Validating only the controller but re-trusting data inside async workers—validate again at trust boundaries.

Huge error payloads listing every internal field path—trim for public APIs.


8. Best practices

Pair @Validated on classes with @NotNull Long id on path variables when you need grouped constraints.

i18n message keys in ValidationMessages.properties for user-facing text.


9. Small practice task

Add @Pattern(regexp = "[a-z0-9-]+") on name slug field and observe JSON error output—then map it to a stable problem+json body in Chapter 127.


Beginner tip

If @Valid “does nothing”, you forgot spring-boot-starter-validation on the classpathdependency first, annotations second.