Динамическая конфигурация проверки DTO, полученная из базы данных
1. обзор
В этом уроке мы рассмотрим, как можноcreate a custom validation annotation that uses a regular expression retrieved from a database to match against the field value.
Мы будем использовать Hibernate Validator в качестве базовой реализации.
2. Maven Зависимости
Для разработки нам понадобятся следующие зависимости:
org.springframework.boot
spring-boot-starter-thymeleaf
2.0.1.RELEASE
org.springframework.boot
spring-boot-starter-data-jpa
2.0.1.RELEASE
Последние версииspring-boot-starter-thymeleaf,spring-boot-starter-data-jpa можно загрузить с Maven Central.
3. Пользовательская аннотация проверки
В нашем примере мы создадим настраиваемую аннотацию с именем@ContactInfo, которая будет проверять значение на соответствие регулярному выражению, полученному из базы данных. Затем мы применим эту проверку к полюcontactInfo класса POJO с именемCustomer.
Чтобы получить регулярные выражения из базы данных, мы будем моделировать их как класс сущностиContactInfoExpression.
3.1. Модели данных и репозиторий
Давайте создадим классCustomer с полямиid иcontactInfo:
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String contactInfo;
// standard constructor, getters, setters
}
Затем давайте взглянем на классContactInfoExpression, который будет содержать значения регулярного выражения в свойстве с именемpattern:
@Entity
public class ContactInfoExpression {
@Id
@Column(name="expression_type")
private String type;
private String pattern;
//standard constructor, getters, setters
}
Затем давайте добавим интерфейс репозитория на основе Spring Data для управления сущностямиContactInfoExpression:
public interface ContactInfoExpressionRepository
extends Repository {
Optional findById(String id);
}
3.2. Настройка базы данных
Для хранения регулярных выражений мы будем использовать базу данныхH2 в памяти со следующей конфигурацией сохраняемости:
@EnableJpaRepositories("com.example.dynamicvalidation.dao")
@EntityScan("com.example.dynamicvalidation.model")
@Configuration
public class PersistenceConfig {
@Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.H2)
.addScript("schema-expressions.sql")
.addScript("data-expressions.sql")
.build();
return db;
}
}
Два упомянутых скрипта используются для создания схемы и вставки данных в таблицуcontact_info_expression:
CREATE TABLE contact_info_expression(
expression_type varchar(50) not null,
pattern varchar(500) not null,
PRIMARY KEY ( expression_type )
);
Скриптdata-expressions.sql добавит три записи для представления типовemail,phone, иwebsite. Они представляют собой регулярные выражения для проверки того, что значением является действительный адрес электронной почты, действительный номер телефона в США или действительный URL:
insert into contact_info_expression values ('email',
'[a-z0-9!#$%&*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?')
insert into contact_info_expression values ('phone',
'^([0-9]( |-)?)?(\(?[0-9]{3}\)?|[0-9]{3})( |-)?([0-9]{3}( |-)?[0-9]{4}|[a-zA-Z0-9]{7})$')
insert into contact_info_expression values ('website',
'^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$')
3.3. Создание пользовательского валидатора
Давайте создадим классContactInfoValidator, который содержит фактическую логику проверки. Следуя рекомендациям спецификации Java Validation, классimplements the ConstraintValidator interface и переопределяет методisValid().
Этот класс получит значение типа контактной информации, используемого в настоящее время -email,phone, илиwebsite, - которое установлено в свойстве с именемcontactInfoType, а затем будет использовать его для получить значение регулярного выражения из базы данных:
public class ContactInfoValidator implements ConstraintValidator {
private static final Logger LOG = Logger.getLogger(ContactInfoValidator.class);
@Value("${contactInfoType}")
private String expressionType;
private String pattern;
@Autowired
private ContactInfoExpressionRepository expressionRepository;
@Override
public void initialize(ContactInfo contactInfo) {
if (StringUtils.isEmptyOrWhitespace(expressionType)) {
LOG.error("Contact info type missing!");
} else {
pattern = expressionRepository.findById(expressionType)
.map(ContactInfoExpression::getPattern).get();
}
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (!StringUtils.isEmptyOrWhitespace(pattern)) {
return Pattern.matches(pattern, value);
}
LOG.error("Contact info pattern missing!");
return false;
}
}
СвойствуcontactInfoType можно задать в файлеapplication.properties одно из значенийemail,phone илиwebsite:
contactInfoType=email
3.4. Создание аннотации пользовательского ограничения
А теперь давайте создадим интерфейс аннотации для нашего настраиваемого ограничения:
@Constraint(validatedBy = { ContactInfoValidator.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface ContactInfo {
String message() default "Invalid value";
Class>[] groups() default {};
Class extends Payload>[] payload() default {};
}
3.5. Применение настраиваемого ограничения
Наконец, давайте добавим аннотации проверки в полеcontactInfo нашего классаCustomer:
public class Customer {
// ...
@ContactInfo
@NotNull
private String contactInfo;
// ...
}
4. Контроллер Spring и HTML-форма
Чтобы протестировать нашу аннотацию проверки, мы создадим сопоставление запросов Spring MVC, которое использует аннотацию@Valid для запуска проверки объектаCustomer:
@PostMapping("/customer")
public String validateCustomer(@Valid Customer customer, BindingResult result, Model model) {
if (result.hasErrors()) {
model.addAttribute("message", "The information is invalid!");
} else {
model.addAttribute("message", "The information is valid!");
}
return "customer";
}
ОбъектCustomer отправляется контроллеру из HTML-формы:
Чтобы подвести итоги, мы можем запустить наше приложение как приложение Spring Boot:
@SpringBootApplication
public class DynamicValidationApp {
public static void main(String[] args) {
SpringApplication.run(DynamicValidationApp.class, args);
}
}
5. Заключение
В этом примере мы показали, как мы можем создать пользовательскую аннотацию проверки, которая динамически извлекает регулярное выражение из базы данных и использует его для проверки аннотированного поля.
Полный исходный код примера можно найти вover on GitHub.