← назад к разделу

Данные, которые приходят из запроса, могут быть чем угодно. Bean Validation — стандарт Jakarta EE, который позволяет описать правила прямо в классе DTO и не засорять бизнес-логику проверками if (name == null || name.isBlank()).

Зачем это нужно

Без валидации каждый контроллер или сервис вынужден сам проверять входные данные. Получается повторяющийся, хрупкий код. Bean Validation решает задачу иначе: правила объявляются один раз — в классе-объекте передачи данных (DTO) — и Spring применяет их автоматически до того, как управление попадёт в метод контроллера.

Зависимость уже включена в spring-boot-starter-web, ничего отдельно подключать не нужно.

Аннотации: что ставить на поля DTO

Все аннотации из пакета jakarta.validation.constraints.*:

public record CreateUserRequest(
    @NotBlank String username,
    @Email @NotBlank String email,
    @Size(min = 8, max = 72) String password,
    @Min(0) @Max(150) int age,
    @Pattern(regexp = "\\+\\d{7,15}") String phone
) {}

Короткая формула: @NotNull — значение не null; @NotBlank — строка не null и не пустая (включая пробелы); @Size — длина строки или коллекции; @Min / @Max — диапазон числа; @Email — формат электронной почты; @Pattern — произвольное регулярное выражение.

Каждая аннотация принимает атрибут message для переопределения текста ошибки — подробнее о кастомных сообщениях в статье про собственные аннотации и сообщения.

@Valid в контроллере запускает проверку

Одна аннотация @Valid перед параметром — и Spring проверяет всё DTO перед вызовом метода:

@RestController
@RequestMapping("/users")
public class UserController {

    @PostMapping
    public ResponseEntity<Void> create(@Valid @RequestBody CreateUserRequest request) {
        // сюда код попадает только если все проверки прошли
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }
}

Без @Valid аннотации на полях DTO ни на что не влияют — Spring просто не запустит проверку.

Вложенные объекты и каскад

Если DTO содержит другой объект, аннотации на его полях не сработают автоматически. Нужно поставить @Valid на само поле — тогда Spring проверит вложенный объект каскадно:

public record CreateOrderRequest(
    @NotNull @Valid AddressRequest address,
    @Size(min = 1) List<@Valid OrderItemRequest> items
) {}
public record AddressRequest(
    @NotBlank String city,
    @NotBlank String street
) {}

Без @Valid на поле address проверка остановится на уровне CreateOrderRequest и не зайдёт внутрь AddressRequest.

Группы валидации

Иногда одно поле нужно проверять по-разному в зависимости от операции. Для этого Bean Validation поддерживает группы:

public interface OnCreate {}
public interface OnUpdate {}

public record UserRequest(
    @NotNull(groups = OnCreate.class) String username,
    @NotBlank(groups = {OnCreate.class, OnUpdate.class}) String email
) {}

В контроллере вместо @Valid используется @Validated с указанием группы:

@PostMapping
public ResponseEntity<Void> create(@Validated(OnCreate.class) @RequestBody UserRequest request) { ... }

@PutMapping("/{id}")
public ResponseEntity<Void> update(@PathVariable Long id,
                                   @Validated(OnUpdate.class) @RequestBody UserRequest request) { ... }

На практике группы нужны редко — чаще достаточно отдельных DTO для создания и обновления.

Что происходит при ошибке

Если хотя бы одно ограничение нарушено, Spring выбрасывает MethodArgumentNotValidException. По умолчанию это 400 Bad Request с объёмным телом ответа, не очень удобным для клиента.

Чтобы вернуть понятную ошибку в едином формате, нужен глобальный обработчик — @RestControllerAdvice с методом на MethodArgumentNotValidException. Как его написать, описано в статье Обработка ошибок REST API в Java.

Глубокие правила оформления валидации (какой слой отвечает за что, когда @Validated на сервисе, а когда нет) — в стайл-гайде по валидации.

Коротко

  • @NotBlank, @Size, @Email, @Min/@Max, @Pattern — аннотации из jakarta.validation.constraints.* описывают правила прямо в DTO.
  • @Valid перед параметром метода запускает проверку; без него аннотации не работают.
  • Для вложенных объектов нужен @Valid на самом поле — иначе проверка не уйдёт глубже.
  • При нарушении Spring выбрасывает MethodArgumentNotValidException — обрабатывается в @RestControllerAdvice.
  • Группы валидации (@Validated(Group.class)) нужны редко; в большинстве случаев лучше использовать отдельные DTO.

Что почитать дальше

  • Где проводить валидацию в Spring-приложении — какой слой за что отвечает.
  • Собственные аннотации и сообщения об ошибках — как выйти за рамки стандартных ограничений.
  • Обработка ошибок REST API в Java — как красиво вернуть 400 с деталями нарушений.