DTO и @Valid
DTO (Data Transfer Object) — отдельный класс для передачи данных через API, не тот же класс, что хранится в базе (@Entity). Разделение нужно, чтобы внутренняя структура базы не “протекала” наружу в публичный API.
// Entity — то, что хранится в базе
@Entity
class User {
@Id Long id;
String name;
String passwordHash; // это точно не должно уходить наружу!
}
// DTO — то, что видит клиент
record UserDto(Long id, String name) {} // без passwordHash
@Valid — автоматическая проверка входных данных по правилам, описанным аннотациями прямо на полях DTO:
record CreateUserRequest(
@NotBlank String name,
@Email String email,
@Min(18) int age
) {}
@PostMapping
User createUser(@Valid @RequestBody CreateUserRequest request) {
// если данные не прошли проверку — Spring сам вернёт 400 Bad Request,
// метод даже не вызовется
}
Копнуть глубже
Зачем DTO, если можно отдавать @Entity напрямую:
- Безопасность — не утекают чувствительные поля (пароли, внутренние флаги);
- Гибкость API — можно менять структуру базы, не ломая контракт API, и наоборот;
- Защита от over-posting — если бы клиент мог напрямую слать
@Entityв@RequestBody, он мог бы случайно (или специально) передать поле типаisAdmin: true, которого по сути не должно быть в запросе создания пользователя.
Частые аннотации валидации: @NotNull, @NotBlank (строка не пустая), @Min/@Max, @Email, @Size(min, max), @Pattern(regexp). Если валидация не пройдена, по умолчанию Spring бросает MethodArgumentNotValidException — её удобно ловить через @ExceptionHandler (см. тему «ExceptionHandler») и вернуть понятное клиенту сообщение об ошибке вместо стандартного стектрейса.
🎤 Закрыл тему, если можешь объяснить:
• зачем нужен отдельный DTO вместо Entity напрямую;
• как `@Valid` автоматически проверяет входные данные (если дошёл до 2-го слоя).
• как `@Valid` автоматически проверяет входные данные (если дошёл до 2-го слоя).