@Valid on a request body triggers Bean Validation, and @Valid on a nested field requests validation of that nested object.
Spring MVC nested validation: reject a bad line before the handler
The cascade is explicit
The request wrapper has a nonblank receipt ID. Its nested line has a nonblank SKU and a positive quantity. The test accepts a valid object, rejects quantity zero, and rejects a blank receipt ID with 400. The handler runs only for the valid request.
A missing line is a different case: @Valid alone does not require the nested object to exist. Add @NotNull when a line is mandatory. A nonblank ID is not proof that the receipt exists or belongs to the caller; that requires separate service and authorization checks.
Keep error output controlled
The fixture verifies status, not a production problem body. Error responses should avoid echoing sensitive raw values and should use field identifiers that clients can handle. Problem details covers that response shape.
Checked source
static class ReceiptLine {
@NotBlank public String sku;
@Positive public int quantity;
}
static class ReceiptImport {
@NotBlank public String receiptId;
@Valid public ReceiptLine line;
}
@PostMapping(path = "/contract/validated-imports", consumes = "application/json")
String accept(@Valid @RequestBody ReceiptImport request) {
return request.receiptId;
}Verification boundary
NestedReceiptValidationTest.rejectsInvalidNestedFieldsBeforeCallingTheHandler runs in the downloadable Spring source kit. This excerpt omits imports and test setup; the kit includes complete source.
Costs and boundaries
MockMvc checks one valid request and two invalid fields using an in-process validator. It does not prove authorization, persistence, every malformed JSON shape, or a production error contract.
Common Mistakes
- Do not omit @Valid on the nested field when cascade validation is intended.
- Do not assume @Valid means a missing nested object is rejected; add @NotNull if required.
- Do not treat format validation as a tenant-ownership check.
Read next
Spring MVC request validation: reject invalid commands before mutation, Spring MVC ProblemDetail: stable errors without leaking internals, Spring method authorization: reject a cross-tenant read.
