Validating input

Spring Boot · Practical · 12 min

Rejecting bad input at the edge keeps invalid data out of your services and database.

Add spring-boot-starter-validation (Hibernate Validator). Then put constraints on DTO fields or record components: @NotBlank, @NotNull, @Size(max = 100), @Email, @Min, @Max, @Positive, @Past, @Pattern(regexp = ...). In Boot 3 they live in the jakarta.validation.constraints package (not javax).

Trigger validation by annotating the controller parameter with @Valid (or @Validated): create(@Valid @RequestBody CreateUser req). If a constraint fails, the method isn't called and Spring throws MethodArgumentNotValidException, which becomes a 400 Bad Request. Customise the response body with an exception handler (next lesson).

Nested objects are validated only if the field itself is annotated @Valid. For @PathVariable and @RequestParam constraints (@Min(1) @PathVariable long id), Spring MVC 6.1+ applies method validation automatically when constraints are present.

Write custom constraints when the built-ins aren't enough: an annotation plus a ConstraintValidator class. Cross-field rules (end after start) can be a constraint on the class.

Validation in the web layer complements, but doesn't replace, database constraints and business-rule checks in services. You can also validate service methods with @Validated on the class, and @ConfigurationProperties classes the same way.

Example

record CreateUser(
    @NotBlank @Size(max = 50) String name,
    @NotBlank @Email String email,
    @Min(13) int age,
    @Valid @NotNull Address address) {}

record Address(@NotBlank String city, @Pattern(regexp = "\\d{4}") String zip) {}

@RestController
@RequestMapping("/api/users")
class UserController {
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    CreateUser create(@Valid @RequestBody CreateUser req) {
        return req;                     // only reached when everything is valid
    }

    @GetMapping("/{id}")
    String get(@PathVariable @Min(1) long id) { return "user " + id; }
}
// POST {"name":"","email":"x","age":10,"address":{"city":"Yerevan","zip":"12"}}
//  -> 400, errors on name, email, age, address.zip
// GET /api/users/0 -> 400 (method validation)

Remember

  • Constraints like @NotBlank, @Email, @Size live in jakarta.validation
  • @Valid on the parameter triggers validation; failures give 400
  • Cascade into nested objects with @Valid on the field
  • Validation at the edge complements database constraints

Watch out for

  • Forgetting @Valid on the @RequestBody parameter, so all the constraint annotations are silently ignored.

Check yourself

  1. A record has @NotBlank String name but the controller parameter is just @RequestBody CreateUser req. What happens with an empty name?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.spring.io/spring-boot/reference/io/validation.html

Builds on

Unlocks

Nothing builds on this yet.