Осторожно: @Size не проверяет на null! Как я пропустил баг
Забыли добавить валидацию на фронтенде и рассчитывали на @Size(min = 1) в Spring? Остерегайтесь! Эта аннотация пропускает null, и ваше "обязательное" поле может прийти пустым. Делюсь реальным кейсом из продакшна, объясняю, почему так происходит, и как правильно использовать @NotBlank, @NotEmpty и @NotNull, чтобы избежать ошибок валидации.
258 открытий2К показов
Разрабатывая веб-приложения на Java Spring, мы часто сталкиваемся с необходимостью валидации входных данных. Одна из самых распространённых задач — убедиться, что обязательное текстовое поле заполнено и соответствует определённым критериям, например, длине. Кажется, что решение очевидно: используем аннотацию @Size. Но здесь нас поджидает одна неприятность.
Недавно я столкнулся с одной проблемой на реальном проекте. У меня есть сайт: бэкенд написан на Spring, а фронтенд — на Angular. Я добавлял новую форму для отправки отзыва пользователя и, в спешке, забыл добавить валидацию на одно из полей формы на фронтенде.
Я хотел, чтобы поле «комментарий» было обязательным и имело длину от 1 до 100 символов. На бэкенде использовал аннотацию @Size(min = 1, max = 100) к обязательным полям, например для поля `comment` в моем DTO имеет такой вид:
Тест с пустой строкой {"comment": ""} был корректно отклонён. Однако, когда я отправил запрос без поля "comment" вообще ( {} ), валидация прошла успешно, и поле «comment» пришло как null. Это была серьёзная ошибка — поле, которое по логике было обязательным, могло быть пропущено. Т.е. могли потеряться важные для меня данные.
Почему так происходит?
Причина кроется в спецификации JSR-303 (Bean Validation). Документация (Javadoc) для аннотации @Size прямо указывает: «null elements are considered valid». Это означает, что если значение поля null, валидатор @Size считает его валидным и не выполняет проверку длины.
Это поведение подтверждается и в исходном коде валидатора. Метод, отвечающий за проверку строки, сначала проверяет значение на null, и если оно null, сразу возвращает true, не переходя к проверке размера. Это стандартное поведение для многих аннотаций валидации, таких как @Min, @Max, @Pattern и т.д. Они проверяют качество значения, но не его наличие.
Предлагаемое решение
Чтобы поле было действительно обязательным (не null и не пустое), необходимо использовать аннотации, которые проверяют наличие значения - это @NotNull, @NotEmpty, @NotBlank . Для строк лучшим выбором является комбинация:
Понимание разницы между этими аннотациями критически важно:
- @NotNull: Гарантирует, что значение поля не является null. Однако оно не проверяет пустые строки или строки из пробелов и будут считаться валидными.
- @NotEmpty: Гарантирует, что значение не null и не пустое (для строки длина > 0). Это означает, что "" будет отклонено, но строка из одних пробелов " " пройдёт валидацию, так как её длина больше 0.
- @NotBlank: Предназначена исключительно для String. Гарантирует, что строка не null, не пустая и после удаления пробелов с начала и конца (trim) имеет длину больше 0. Это означает, что null, "" и " " будут отклонены. Именно это поведение обычно ожидается от "обязательного текстового поля".
Красивое возвращение ошибок
Для того чтобы клиент (в моём случае, Angular-приложение) получал понятные сообщения об ошибках валидации, я настроил глобальный обработчик исключений с помощью @ControllerAdvice. Когда валидация не проходит, Spring выбрасывает MethodArgumentNotValidException. Далее, я его перехватываю и возвращаю структурированный ответ.
Angular-приложение может легко обработать этот JSON и отобразить пользователю понятное сообщение об ошибке.
Выводы
Аннотация @Size сама по себе не делает поле обязательным. Она проверяет только длину, если значение не null. Для обязательных строковых полей всегда используйте @NotBlank. Для других типов данных используйте @NotNull.
Не полагайтесь на @Size(min = 1) как на замену проверки на null или пустую строку. Эта ловушка, в которую я сам попался, может привести к консистентным данным в вашей системе и потенциальным уязвимостям, если бэкенд будет работать с неожиданными null значениями. Всегда явно указывайте, что поле является обязательным, с помощью соответствующих аннотаций.
Этот материал — не открытие века, а скорее напоминание как себе, так и коллегам. Даже опытные разработчики иногда могут упустить из виду такие нюансы. Я столкнулся с этой проблемой на проекте, и она стоила мне времени на отладку и тестирование.
258 открытий2К показов



