Что такое валидация на уровне контроллера в Spring?

Ответ

Валидация на уровне контроллера — это практика проверки корректности входящих HTTP-запросов (тела, параметров, заголовков) непосредственно в точке входа, до передачи данных в сервисный слой.

Реализация в Spring (с использованием Bean Validation):

  1. Аннотируйте DTO-объект ограничениями из jakarta.validation (или javax.validation).
  2. Используйте аннотацию @Valid (или @Validated) перед параметром метода контроллера.

Пример:

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

    @PostMapping
    public ResponseEntity<UserResponse> createUser(@Valid @RequestBody UserCreateRequest request) {
        // Бизнес-логика выполняется только после успешной валидации
        return ResponseEntity.ok(userService.create(request));
    }
}

// DTO с ограничениями
public class UserCreateRequest {
    @NotNull
    @Size(min = 2, max = 100)
    private String name;

    @NotNull
    @Email
    private String email;

    @Min(18)
    private Integer age;
    // getters/setters
}

Что происходит при ошибке? Spring выбрасывает исключение MethodArgumentNotValidException. Его обычно обрабатывают в @ControllerAdvice, возвращая клиенту структурированное сообщение об ошибках.

Преимущества:

  • Ранний отказ: Некорректные данные отсекаются сразу.
  • Чистота сервисов: Бизнес-логика не засоряется проверками входных данных.
  • Стандартизация: Единый механизм валидации для всех контроллеров.

Ответ 18+ 🔞

Слушай, а вот есть такая штука — валидация на уровне контроллера. Ну, это когда ты проверяешь, что тебе прилетело в HTTP-запросе, прямо на входе, блядь, пока ещё эти данные не уползли в сервисы и не начали там какую-то дичь творить.

В Спринге это делается проще пареной репы, если использовать Bean Validation. Сейчас объясню на пальцах, ёпта.

Вот смотри: берёшь свой DTO, этот... объект, в который данные мапятся. И начинаешь его разрисовывать аннотациями из jakarta.validation. Типа, «эй, поле, ты не должно быть пустым» или «эй, почта, будь добр, выгляди как почта, а не как набор случайных символов».

Потом в контроллере, перед параметром, куда этот DTO прилетает, ставишь волшебную аннотацию @Valid. И всё, блядь! Спринг сам всё проверит. Если данные — говно, то в сервисный слой они даже не пролезут, их нахуй завернут обратно.

Вот, смотри пример, чтобы совсем понятно было:

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

    @PostMapping
    public ResponseEntity<UserResponse> createUser(@Valid @RequestBody UserCreateRequest request) {
        // Сюда выполнение дойдёт только если валидация прошла. Иначе — пиздец и исключение.
        return ResponseEntity.ok(userService.create(request));
    }
}

// А это сам DTO, наш подопытный кролик
public class UserCreateRequest {
    @NotNull // Не хуй тут будет null!
    @Size(min = 2, max = 100) // И длинна от 2 до 100 символов, блядь
    private String name;

    @NotNull
    @Email // Это чтоб реальная почта была, а не "вася@пук.сом"
    private String email;

    @Min(18) // Меньше 18? Иди нахуй, взрослей сначала.
    private Integer age;
    // геттеры/сеттеры тут
}

А что, если данные всё-таки кривые? О, тут интересно! Спринг, такой весь из себя принципиальный, выкинет исключение MethodArgumentNotValidException. Его обычно ловят в каком-нибудь @ControllerAdvice, разбирают на запчасти и отправляют клиенту красивый, структурированный ответ, типа: «дружище, у тебя тут три ошибки, вот они, исправляй».

И зачем этот цирк, спросишь? А преимущества-то, блядь, овердохуищные:

  • Ранний отказ. Неправильные данные отшибаются на самом пороге. Зачем их тащить дальше, тратить ресурсы?
  • Чистота в сервисах. Твоя бизнес-логика не засрана тысячей if-else на проверку входящего говна. Она занимается своим делом.
  • Стандартизация. Один раз настроил — и все контроллеры работают по одному сценарию. Красота, ёпта!

В общем, вещь полезная. Не пренебрегай, а то потом будешь в сервисах ошибки ловить, которые можно было отсечь ещё на подходе.