какая аннотация задает правило валидации размеров полей в сущности связанной с бд

Валидация данных в Spring Boot

Нередко пользователи пытаются передать в приложение некорректные данные. Это происходит либо из злого умысла, либо по ошибке. Поэтому стоит проверять данные на соответствие бизнес-требованиям.

Эту задачу решает Bean Validation. Он интегрирован со Spring и Spring Boot. Hibernate Validator считается эталонной реализацией Bean Validation.

Основы валидации Bean

Для проверки данных используются аннотации над полями класса. Это декларативный подход, который не загрязняет код.

При передаче размеченного таким образом объекта класса в валидатор, происходит проверка на ограничения.

Настройка

Добавьте следующие зависимости в проект:

Валидация в Spring MVC Controller

Сначала данные попадают в контроллер. У входящего HTTP-запроса возможно проверить следующие параметры:

Рассмотрим каждый из них подробнее.

Валидация тела запроса

Тело запроса POST и PUT обычно содержит данные в формате JSON. Spring автоматически сопоставляет входящий JSON с объектом Java.

Проверяем соответствует ли входящий Java объект нашим требованиям.

Контроллер REST принимает объект Input и выполняет проверку:

Если класс содержит поле с другим классом, который тоже необходимо проверить — это поле необходимо пометить аннотацией Valid.

Исключение MethodArgumentNotValidException выбрасывается, когда объект не проходит проверку. По умолчанию, Spring переведет это исключение в HTTP статус 400.

Проверка переменных пути и параметров запроса

Проверка переменных пути и параметров запроса работает по-другому.

Вместо аннотации поля класса, как описано выше, добавляют аннотацию ограничения (в данном случае @Min ) непосредственно к параметру метода в контроллере Spring:

Обратите внимание, что необходимо добавить @Validated Spring в контроллер на уровне класса, чтобы сказать Spring проверять ограничения на параметрах метода.

В этом случае аннотация @Validated устанавливается на уровне класса, даже если она присутствует на методах.

Вернем HTTP статус 400, так как клиент предоставил недействительный параметр. Для этого добавляем пользовательский обработчик исключений в контоллер:

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

Валидация в сервисном слое

Аннотация @Validated устанавливается только на уровне класса, так что не ставьте ее на метод в данном случае.

Валидация сущностей JPA

Persistence Layer это последняя линия проверки данных. По умолчанию Spring Data использует Hibernate, который поддерживает Bean Validation из коробки.

Обычно мы не хотим делать проверку так поздно, поскольку это означает, что бизнес-код работал с потенциально невалидными объектами, что может привести к непредвиденным ошибкам.

Bean Validation запускается Hibernate только после того как EntityManager вызовет flush.

Валидация конфигурации приложения

Spring Boot аннотация @ConfigurationProperties используется для связывания свойств из application.properties с Java объектом.

Данные из application необходимы для стабильной работы приложения. Bean Validation поможет обнаружить ошибку в этих данных при старте приложения.

Допустим имеется следующий конфигурационный класс:

При попытке запуска с недействительным адресом электронной почты получаем ошибку:

Стандартные ограничения

Каждая аннотация имеет следующие поля:

Рассмотрим популярные ограничения.

@NotNull и @Null

@NotNull — аннотированный элемент не должен быть null. Принимает любой тип.
@Null — аннотированный элемент должен быть null. Принимает любой тип.

@NotBlank и @NotEmpty

@NotBlank применяется только к строкам и проверяет, что строка не пуста и не состоит только из пробелов.

Аннотация @Size(min=6) пропустит строку состоящую из 6 пробелов и/или символов переноса строки, а @NotBlank не пропустит.

Размер аннотированного элемента должен быть между указанными границами, включая сами границы. null элементы считаются валидными.

Добавление пользовательского валидатора

Если имеющихся аннотаций ограничений недостаточно, то создайте новые.

В классе Input использовалось регулярное выражение для проверки того, что строка является IP адресом. Регулярное выражение не является полным: оно позволяет сокеты со значениями больше 255, таким образом «111.111.111.333» будет считаться действительным.

Давайте напишем валидатор, который реализует эту проверку на Java. Потому что как говорится, до решения проблемы регулярным выражением у вас была одна проблема, а теперь стало двe 🙂

Сначала создаем пользовательскую аннотацию @IpAddress :

Реализация валидатора выглядит следующим образом:

Принудительный вызов валидации

Для принудительного вызова проверки, без использования Spring Boot, создайте валидатор вручную.

Тем не менее, Spring Boot предоставляет предварительно сконфигурированный экземпляр валидатора. Внедрив этот экземпляр в сервис не придется создавать его вручную.

Когда этот сервис внедряется Spring, в конструктор автоматически вставляется экземпляр валидатора.

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

Некоторые объекты участвуют в разных вариантах использования.

Возьмем типичные операции CRUD: при обновлении и создании, скорее всего, будет использоваться один и тот же класс. Тем не менее, некоторые валидации должны срабатывать при различных обстоятельствах:

Функция Bean Validation, которая позволяет нам внедрять такие правила проверки, называется «Validation Groups».

Для нашего примера CRUD определим два маркерных интерфейса OnCreate и OnUpdate :

Затем используем эти интерфейсы с любой аннотацией ограничения:

Это позволит убедиться, что id пуст при создании и заполнен при обновлении.

Spring поддерживает группы проверки только с аннотацией @Validated

Обратите внимание, что аннотация @Validated применяется ко всему классу. Чтобы определить, какая группа проверки активна, она также применяется на уровне метода.

Использование групп проверки может легко стать анти-паттерном. При использовании групп валидации сущность должна знать правила валидации для всех случаев использования (групп), в которых она используется.

Возвращение структурных ответов на ошибки

Когда проверка не удается, лучше вернуть клиенту понятное сообщение об ошибке. Для этого необходимо вернуть структуру данных с сообщением об ошибке для каждой проверки, которая не прошла валидацию.

Сначала нужно определить эту структуру данных. Назовем ее ValidationErrorResponse и она содержит список объектов Violation :

Источник

Валидация DTO в Spring Boot

какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть картинку какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Картинка про какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд

Во многих веб-приложениях есть формы, на которых пользователь вводит данные (например, форма регистрации на сайте). Почти всегда нужно проводить валидацию этих данных: заполнены ли обязательные поля, записан ли email и телефон в нужном формате и так далее.

В первую очередь валидация выполняется на фронтенде, однако при желании подкованный пользователь может её обойти и послать запрос с невалидными данными. Таким образом бэкенд не может быть уверен в корректности данных, полученных от пользователя. Соответственно необходимо валидировать данные на бэкенде.

В данной статье будет рассмотрена валидация полей ДТО (DTO – Data Transfer Object) с использованием пакета javax.validation.

Создание приложения

Для создания приложения воспользуемся Spring Initializr по адресу https://start.spring.io. Заполним поля group и artifact, а затем добавим в проект зависимости Spring Web и Lombok. Скачаем проект и откроем его в IDE.

Мы разработаем простое приложение, которое будет имитировать сохранение информации о сотрудниках некой организации. Состоять наше приложение будет из следующих частей:

ДТО

Создадим в проекте класс EmployeeDto:

Аннотации над классом относятся к библиотеке Lombok. Благодаря @Data будут сгенерированы геттеры и сеттеры для всех полей, а @AllArgsConstructor создаст конструктор, инициализирующий все поля класса.

Аннотации над полями – это основа нашей валидации. Каждая аннотация задаёт своё правило:

@NotBlank: поле обязательно для заполнения.
@Email: значение должно по формату подходить для использования в качестве email.
@Pattern: значение должно удовлетворять регулярному выражению.
@Past: дата/время должно быть в прошлом

REST-контроллер

Создим класс EmployeeController:

Это простой контроллер, который обрабатывает запросы, поступающие методом POST на адрес http://localhost:8080/employees.

Аннотация @Valid – ключевой момент: благодаря ей будет запускаться валидация аргумента dto в соответствии с аннотациями @NotBlank, @Email и т.д, которые мы описали в EmployeeDto.

Тест контроллера

Создадим класс EmployeeControllerTest для проверки работы контроллера:

Использование аннотации @WebMvcTest над классом позволяет не поднимать весь Spring-контекст, а инициализировать только веб-слой приложения (в частности создать контроллеры). Параметр controllers говорит о том, что нужно создать только указанные контроллеры. Если его не указывать, то будут созданы все контроллеры приложения.

Класс MockMvc, входящий состав в Spring, предоставляет удобные средства для тестирования контроллеров. Используя его можно не поднимать реальный сервер, такой как Tomcat, а тестировать с того момента, где Spring передаёт запрос в наш контроллер. Таким образом для контроллера всё будет выглядеть так, как будто был отправлен и получен реальный HTTP-запрос, но при этом не нужно тратить ресурсы на поднятие сервера.

Добавим тест, в котором будем проверять поведение контроллера, когда в ДТО нет поля name:

В этом тесте отправляется неполная версия EmployeeDto в виде JSON на адрес /employees. В отправляемом JSON нет поля name. А поскольку это поле обязательно для заполнения, то валидация ДТО должна “свалиться” с кодом 400 (Bad Request). Запустите тест – он должен пройти успешно.

Если мы хотим убедиться, что ошибка валидации вызвана именно отсутствием поля name, а не чем-то другим, мы можем модифицировать наш тест:

Также мы можем предусмотреть ситуацию, когда контроллер ничего не вернёт:

Принудительный запуск валидации

Аннотация @Valid выглядит так, будто одно лишь её наличие запускает валидацию, однако это не совсем так. Убедиться в этом можно, “вручную” вызвав метод контроллера.

Напишем новый тест:

Он “свалится”, поскольку исключение MethodArgumentNotValidException не будет брошено. В чём же причина? Дело в том, что просто добавить аннотации @Valid и @NotBlank – недостаточно. Вся “магия” происходит внутри Spring, а когда мы вызываем метод “вручную”, то Spring не задействован.

Но мы можем принудительно запустить валидацию. Изменим тест:

Этот тест пройдёт успешно, а в переменной violations будет один элемент с информацией об ошибке валидации поля name.

Заключение

Рассмотренные варианты валидации – это неполный список того, что умеет пакет javax.validation. Благодаря ему можно быстро настроить валидацию в Spring Boot-приложении и сократить время разработки.

Код приложения из этой статьи можно взять на Github.

Источник

Валидация данных в Spring Boot

Нередко пользователи пытаются передать в приложение некорректные данные. Это происходит либо из злого умысла, либо по ошибке. Поэтому стоит проверять данные на соответствие бизнес-требованиям.

какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть картинку какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Картинка про какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд

Нередко пользователи пытаются передать в приложение некорректные данные. Это происходит либо из злого умысла, либо по ошибке. Поэтому стоит проверять данные на соответствие бизнес-требованиям.

Эту задачу решает Bean Validation. Он интегрирован со Spring и Spring Boot. Hibernate Validator считается эталонной реализацией Bean Validation.

Для проверки данных используются аннотации над полями класса. Это декларативный подход, который не загрязняет код.

При передаче размеченного таким образом объекта класса в валидатор, происходит проверка на ограничения.

Добавьте стартер в проект, чтобы включить валидацию:

Валидация в Controller

Сначала данные попадают в контроллер. У входящего HTTP-запроса возможно проверить следующие параметры:

Рассмотрим каждый из них подробнее.

Валидация тела запроса

Тело запроса POST и PUT обычно содержит данные в формате JSON. Spring автоматически сопоставляет входящий JSON с объектом Java.

Проверяем соответствует ли входящий Java объект нашим требованиям.

В демонстрационном проекте для удобства вы можете использовать Swagger, о нем я писал в статье: Документирование API с помощью OpenAPI 3 и Swagger. Я же буду использовать Postman.

Вызываем наш POST метод и передаем в него не валидные данные.

какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть картинку какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Картинка про какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд

Postman возвращает нам ошибку, а в консоли получаем исключение, которое сообщает нам что у нас 2 ошибки валидации.

какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть картинку какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Картинка про какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд

Исключение MethodArgumentNotValidException выбрасывается, когда объект не проходит проверку. По умолчанию, Spring переведет это исключение в HTTP статус 400.

Исключение информативное, но очень тяжелое для восприятия, пользователь при этом не получает никакой информации об ошибке. Далее мы рассмотрим, как это исправить.

Проверка переменных пути и параметров запроса

Вместо аннотации поля класса, как описано выше, добавляют аннотацию ограничения (в данном случае @Min ) непосредственно к параметру метода в контроллере:

Обратите внимание, что необходимо добавить @Validated в контроллер на уровне класса, чтобы проверять параметры метода. В этом случае аннотация @Validated устанавливается на уровне класса, даже если она присутствует на методах.

какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть картинку какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Картинка про какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд

Валидация в сервисном слое

Аннотация @Validated устанавливается только на уровне класса, так что не ставьте ее на метод в данном случае.

Валидация сущностей JPA

Persistence Layer – это последняя линия проверки данных. По умолчанию Spring Data использует Hibernate, который поддерживает Bean Validation из коробки.

Обычно мы не хотим делать проверку так поздно, поскольку это означает, что бизнес-код работал с потенциально невалидными объектами, что может привести к непредвиденным ошибкам.

Допустим, необходимо хранить объекты нашего класса PersonDto в базе данных.

Bean Validation запускается Hibernate только после того как EntityManager вызовет flush.

Конкретизация ошибок

Когда проверка не удается, лучше вернуть клиенту понятное сообщение об ошибке. Для этого необходимо вернуть структуру данных с сообщением об ошибке для каждой проверки, которая не прошла валидацию.

Сначала нужно определить эту структуру данных. Назовем ее ValidationErrorResponse и она содержит список объектов Violation :

какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть картинку какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Картинка про какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд

Можно изменить сообщение об ошибке с помощью параметра message у аннотации.

Валидация конфигурации приложения

Spring Boot аннотация @ConfigurationProperties используется для связывания свойств из application.properties с Java объектом.

Данные из application необходимы для стабильной работы приложения. Bean Validation поможет обнаружить ошибку в этих данных при старте приложения.

Допустим имеется следующий конфигурационный класс:

При попытке запуска с недействительным адресом электронной почты получаем ошибку:

Стандартные ограничения

Каждая аннотация имеет следующие поля:

Рассмотрим популярные ограничения.

@NotNull и @Null

@NotBlank и @NotEmpty

@NotBlank применяется только к строкам и проверяет, что строка не пуста и не состоит только из пробелов.

Аннотация @Size(min=6) пропустит строку состоящую из 6 пробелов и/или символов переноса строки, а @NotBlank не пропустит.

Размер аннотированного элемента должен быть между указанными границами, включая сами границы. null элементы считаются валидными.

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

Некоторые объекты участвуют в разных вариантах использования.

Возьмем типичные операции CRUD: при обновлении и создании, скорее всего, будет использоваться один и тот же класс. Тем не менее, некоторые валидации должны срабатывать при различных обстоятельствах:

Функция Bean Validation, которая позволяет нам внедрять такие правила проверки, называется “Validation Groups”.

Для нашего примера CRUD определим два маркерных интерфейса OnCreate и OnUpdate :

Затем используем эти интерфейсы с любой аннотацией ограничения:

Это позволит убедиться, что id пуст при создании и заполнен при обновлении.

Spring поддерживает группы проверки только с аннотацией @Validated

Обратите внимание, что аннотация @Validated применяется ко всему классу. Чтобы определить, какая группа проверки активна, она также применяется на уровне метода.

Использование групп проверки может легко стать анти-паттерном. При использовании групп валидации сущность должна знать правила валидации для всех случаев использования (групп), в которых она используется.

Создание своего ограничения

Если имеющихся аннотаций ограничений недостаточно, то создайте новые.

Напишем валидатор, который будет проверять, что строка начинается с большой буквы.

Сначала создаем пользовательскую аннотацию @CapitalLetter :

Реализация валидатора выглядит следующим образом:

Принудительный вызов валидации

Для принудительного вызова проверки, без использования Spring Boot, создайте валидатор вручную.

Тем не менее, Spring Boot предоставляет предварительно сконфигурированный экземпляр валидатора. Внедрив этот экземпляр в сервис не придется создавать его вручную.

Источник

Spring MVC кастомная аннотация для валидации форм

Вы наверное не раз встречали на сайтах валидаторы форм, которые следят за тем, что бы вы ввели корректную информацию. Но как же сделать валидацию под свои нужды? Именно этот вопрос будет рассматриваться в данном уроке.

Шаг 0

Для того чтобы начинать что то программировать нужно поставить задачу которую в результате написания кода мы будем решать. Так вот наша задача в том, чтобы создать свой валидатор под сои нужды, а именно мы будем создавать валидатор для валидации номера телефона и даты рождения.

Допустим у нас есть сущность User, который должен дать нам свое Имя, номер телефона и указать год рождения. Для этого мы создадим две аннотации с помощью которых и будем выполнять валидацию данных.

Шаг 1

какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть картинку какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Картинка про какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд

Вот у нас и готов проект на который мы будем привязывать форму и её валидацию.

Шаг 2

Теперь добавим нужные нам зависимости в pom.xml:

После этого можно приступать к созданию кастомного валидатора.

Шаг 3

Создаем сущность User:

И немного позже на эту сущность мы будем добавлять аннотации, которые и будут выполнять валидацию данных в экземпляре этой сущности.

Шаг 4

Создаем аннотацию Phone:

какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть картинку какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Картинка про какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд

У вас появится Phone.java, дальше вы должны в нем написать следующий код:

Приведенный выше код в имеет три метода. Эти три метода требуются JSR-303 спец. Если бы наша аннотация принимала любые аргументы, мы бы определили их в теле аннотации, как методы. Мы будем рассматривать этот пример в реализации следующей аннотации.

В message() методе мы можем указать сообщение которое будет выводится при валидации, мы это будем указывать с помощью Spring resource bundle.

Шаг 5

Как вы видите саму аннотацию мы аннотируем еще 4-мя аннотациями:

@Documented – указывает, что помеченная таким образом аннотация должна быть добавлена в javadoc поля/метода.

@Target – указывает, что именно мы можем пометить этой аннотацией.

ElementType.PACKAGE – только для пакетов;
ElementType.TYPE – только для классов;
ElementType.CONSTRUCTOR – только для конструкторов;
ElementType.METHOD – только для методов;
ElementType.FIELD – только для атрибутов(переменных) класса;
ElementType.PARAMATER – только для параметров метода;
ElementType.LOCAL_VARIABLE – только для локальных переменных.

@Retention – позволяет указать жизненный цикл аннотации: будет она присутствовать только в исходном коде, в скомпилированном файле, или она будет также видна и в процессе выполнения.

RetentionPolicy.CLASS – будет присутствовать в скомпилированном файле;
RetentionPolicy.RUNTIME – будет присутствовать только в момент выполнения;
RetentionPolicy.SOURCE – будет присутствовать только в исходном коде.

@Constraint – список реализаций данного интерфейса.

Шаг 6

Теперь создаем реализацию данной аннотации которая указанна в аннотации @Constraint, создаем простой класс с именем PhoneConstraintValidator.java со следующим содержимым:

Давайте посмотрим на приведенный выше код. Как вы видите мы реализуем интерфейс ConstraintValidator, который принимает два типа: тип аннотации который будет поддерживаться, и тип свойства, который он проверяет (в данном примере, телефон, String).

initialize(Phone phone) – этот метод пустой и не имеет реализации, но он может быть использован, для того чтобы сохранить данные из аннотации, во второй аннотации нам пригодится этот метод и мы его реализуем.

А основную логику проверки выполняет метод isValid(String phoneField, ConstraintValidatorContext cxt). Значение поля передается в качестве первого аргумента, как вы можете видеть, я просто проверил, что номер телефона содержатся только цифры, круглые скобки и тире.

Теперь можно проаннотировать поле PhoneNumber в сущности User но мы сделаем это позже.

Шаг 7

Теперь создадим аннотацию @Year:

Обратите внимание на value() метод, который мы будем использовать, чтобы проверить год. Остальная часть кода – это основной шаблон.

Шаг 8

Давайте теперь напишем реализацию данной аннотации. Для этого создаем класс YearConstraintValidator.java со следующим содержимым:

Первое на что нужно обратить внимание это то что теперь мы используем метод initialize(Year year) так как он выполняется первым, мы присваиваем локальной переменной значение с аннотации @Year.

После этого выполняется метод isValid(Date target, ConstraintValidatorContext cxt) метод, который будет проверять на валидность дату, то есть если пользователем будет неправильно указанна дату то вернется false, что будет значить не валидность даты.

Шаг 9

Теперь создаем конфигурационный класс WebConfig.java со следующим содержимым:

Источник

Валидация в Java-приложениях

Этот текст посвящен различным подходам к валидации данных: на какие подводные камни может наткнуться проект и какими методами и технологиями стоит руководствоваться при валидации данных в Java-приложениях.

какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть картинку какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Картинка про какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд

Я часто видел проекты, создатели которых вообще не утруждались выбором подхода к валидации данных. Команды работали над проектом под невероятным давлением в виде сроков и размытых требований, и в итоге у них просто не оставалось времени на точную, последовательную валидацию. Поэтому, код валидации у них разбросан повсюду: в сниппетах Javascript, контроллерах экранов, в бинах бизнес-логики, сущностях предметной области, триггерах и database constraints. В этом коде было полно операторов if-else, он выбрасывал кучу исключений, и попробуй разберись, где там у них валидируется этот конкретный кусок данных… Как результат, по мере развития проекта становится тяжело и дорого соблюдать и требования (зачастую довольно путаные), и единообразие подходов к валидации данных.

Так есть ли какой-то простой и изящный способ валидации данных? Способ, который защитит нас от греха нечитаемости, способ, который соберет всю логику валидации воедино, и который уже создан за нас разработчиками популярных Java-фреймворков?

Да, такой способ существует.

Для нас, разработчиков платформы CUBA, очень важно, чтобы вы могли пользоваться передовыми практиками. Мы считаем, что код валидации должен:

Давайте посмотрим, как это можно реализовать на примере приложения, написанного с использованием фреймворка CUBA Platform. Однако, так как CUBA построена на основе Spring и EclipseLink, большинство используемых здесь приемов будет работать и на любой другой Java платформе, поддерживающей спецификации JPA и Bean Validation.

Валидация с использованием database constraints

Пожалуй, самый распространенный и очевидный способ валидации данных — это использование ограничений на уровне БД, например, флаг required (для полей, значение которых не может быть пустым), длина строки, уникальные индексы и т.д. Такой способ больше всего подходит для корпоративных приложений, так как этот тип ПО обычно строго ориентирован на обработку данных. Тем не менее, даже здесь разработчики часто совершают ошибки, задавая ограничения отдельно для каждого уровня приложения. Чаще всего причина кроется в распределении обязанностей между разработчиками.

Рассмотрим пример, который большинству из нас знаком, некоторым даже по собственному опыту… Если спецификация гласит, что в поле номера паспорта должно быть 10 знаков, весьма вероятно, что проверяться это будет всеми: архитектором БД в DDL, backend-разработчиком в соответствующих Entity и REST сервисах, и наконец, разработчиком UI непосредственно на стороне клиента. Затем это требование меняется, и поле возрастает до 15 знаков. Девопсы меняют значения constraints в БД, но для пользователя ничего не меняется, ведь на стороне клиента ограничение все то же.

Любой разработчик знает, как избежать этой проблемы, — валидация должна быть централизована! В CUBA такая валидация находится в JPA-аннотациях к сущностям. Основываясь на этой метаинформации, CUBA Studio сгенерирует верный DDL-скрипт и применит соответствующие валидаторы на стороне клиента.

какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть картинку какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Картинка про какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд

Если аннотации изменятся, CUBA обновит DDL-скрипты и сгенерирует миграционные скрипты, поэтому в следующий раз при развертывании проекта новые ограничения на основе JPA вступят в силу и в интерфейсе и в базе данных приложения.

Как бы то ни было, в случаях, требующих более сложной логики валидации, вроде проверки поля на минимальное/максимальное значение, валидации при с помощи регулярного выражения, или выполнения кастомной проверки, свойственной только вашему приложению, применяется подход, известный как «Bean Validation».

Bean validation

Всем известно, что хорошей практикой является следование стандартам, имеющим длинный жизненный цикл, чья эффективность доказана на тысячах проектов. Java Bean Validation — это подход, зафиксированный в JSR 380, 349 и 303 и их применениях: Hibernate Validator и Apache BVal.

Хотя этот подход знаком многим разработчикам, его часто недооценивают. Это простой способ встраивать валидацию данных даже в legacy-проекты, который позволяет выстраивать проверки понятно, просто, надежно и максимально близко к бизнес-логике.

Использование Bean Validation дает проекту немало преимуществ:

Когда пользователь отправляет введенную информацию, CUBA Platform (как и некоторые другие фреймворки) запускает Bean Validation автоматически, поэтому он мгновенно выдает сообщение об ошибке, если валидация не прошла, и нам не нужно запускать валидаторы бинов вручную.

Вернемся к примеру с номером паспорта, но на этот раз дополним его несколькими ограничениями сущности Person:

Со всеми этими проверками класс Person будет выглядеть так:

Описание аннотации выглядит так:

Вот и все. С CUBA Platform нам не нужно писать ничего, кроме строки кода, которая заставит нашу кастомную валидацию работать и выдавать пользователю сообщения об ошибке.
Ничего сложного, правда?

Теперь проверим, как это все работает. Здесь у CUBA есть и другие ништяки: она не только показывает пользователю сообщение об ошибке, но и подсвечивает красным поля, не прошедшие bean validation:

какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть картинку какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Картинка про какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд

Не правда ли, изящное решение? Вы получаете адекватное отображение ошибок валидации в UI, добавив лишь пару Java-аннотаций к сущностям предметной области.

Подводя итог раздела, давайте еще раз кратко перечислим плюсы Bean Validation для сущностей:

Но что делать, если нужно установить ограничение на метод, конструктор или REST-адрес для валидации данных, исходящих из внешней системы? Или если нужно декларативно проверить значения параметров метода, не прописывая нудный код с множеством if-else условий в каждом проверяемом методе?

Ответ прост: Bean Validation применим и к методам!

Validation by contract

Иногда бывает нужно пойти дальше валидации состояния модели данных. Многим методам может пойти на пользу автоматическая валидация параметров и возвращаемого значения. Это может быть необходимо не только для проверки данных, идущих в адреса REST или SOAP, но и в тех случаях, когда мы хотим прописать предусловия и постусловия вызовов метода, чтобы убедиться, что введенные данные были проверены до выполнения тела метода, или что возвращаемое значение находится в ожидаемом диапазоне, или нам, например, нужно просто декларативно описать диапазоны значений входных параметров для улучшения читаемости кода.

С помощью bean validation ограничения могут применяться к входным параметрам и возвращаемым значениям методов и конструкторов для проверки предусловий и постусловий их вызовов у любого Java класса. У этого пути есть несколько преимуществ перед традиционными способами проверки правильности параметров и возвращаемых значений:

Используя ‘валидацию по контракту’ мы получаем понятный, компактный и легко поддерживаемый код.

И не забываем, что bean validation наследуется! Другими словами, если мы аннотируем некий класс, или поле, или метод, то на все классы, наследующие данный класс или реализующие данный интерфейс, будет распространяться та же самая валидационная аннотация.

В CUBA приложении эти методы доступны по следующим адресам:

Откроем приложение Postman и убедимся, что валидация работает как надо:

какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть картинку какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Картинка про какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд

Перекрестная валидация параметров поддерживается JSR 349 и 380. Можете ознакомиться с документацией hibernate, чтобы узнать, как реализовать собственную перекрестную валидацию методов класса/интерфейса.

За пределами bean validation

Нет в мире совершенства, вот и у bean validation есть свои недостатки и ограничения:

CUBA Platform предлагает два механизма валидации данных до коммита, которые называются entity listeners и transaction listeners. Рассмотрим их подробнее.

Entity listemers

В CUBA легко создать и подключить entity listener, для этого нужно две вещи:

По сравнению со стандартом JPA (JSR 338, раздел 3.5), listener-интерфейсы CUBA Platform типизированы, поэтому вам не нужно приводить аргумент с типом Object к типу сущности, чтобы начать с ней работать. Платформа CUBA добавляет связанным сущностям или вызывающим EntityManager возможность загружать и изменять другие сущности. Все эти изменения также будут вызывать соответствующий entity listener.

Сама реализация слушателя выглядит так:

Entity listeners — отличный выбор, если:

Transaction listeners

CUBA transaction listeners также действуют в контексте транзакций, но, по сравнению с entity listeners, они вызываются для каждой транзакции базы данных.

Эти дает им супер-силу:

Но это же определяют их недостатки:

Итак, transaction listeners — хорошее решение, когда нужно проинспектировать разные типы сущностей по одному алгоритму, например, проверка всех данных на предмет кибер-мошенничества единым сервисом, который обслуживает все ваши бизнес-объекты.

какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Смотреть картинку какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Картинка про какая аннотация задает правило валидации размеров полей в сущности связанной с бд. Фото какая аннотация задает правило валидации размеров полей в сущности связанной с бд

Заключение

Bean validation (JPA 303, 349 и 980) — это подход, который может служить надежной основой для 95% случаев валидации данных, встречающихся в корпоративном проекте. Главное преимущество такого подхода состоит в том, что большая часть логики валидации сконцентрирована прямо в классах доменной модели. Поэтому ее легко найти, легко читать и легко поддерживать. Spring, CUBA и многие другие библиотеки поддерживают эти стандарты и автоматически выполняют проверки в рамках валидации во время получения данных на UI слое, вызова validated-методов или процесса сохранения данных через ORM, поэтому Bean validation с точки зрения разработчика часто выглядит как магия.

Некоторые разработчики ПО рассматривают валидацию на уровне классов предметной модели как неестественную и слишком сложную, говорят, что проверки данных на уровне UI — достаточно эффективная стратегия. Однако, я считаю, что многочисленные точки валидации в компонентах и контроллерах UI — не самый рациональный подход. К тому же, методы валидации, перечисленные здесь, не выглядят такими неестественными, когда они интегрированы в платформу, в которой есть валидаторы бинов и listener’ы и которая автоматически интегрирует их с клиентским слоем.

В заключение, сформулируем правила, помогающие выбрать лучший метод валидации:

PS: Надеюсь, эта статья освежила ваши знания о различных методах валидации в корпоративных приложениях на Java, и подкинула несколько идей о том, как оптимизировать архитектуру проектов, над которыми вы работаете.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *