«1. Обзор

Одной из особенностей Java JSR 380 является разрешение выражений при интерполяции сообщений проверки с параметрами.

Когда мы используем Hibernate Validator, нам необходимо добавить одну из унифицированных реализаций Java JSR 341 в качестве зависимости к нашему проекту. JSR 341 также называется API языка выражений.

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

В этом коротком руководстве мы рассмотрим, как настроить ParameterMessageInterpolator в Hibernate Validator.

2. Интерполяторы сообщений

Помимо основ проверки Java-бина, MessageInterpolator API Bean Validation представляет собой абстракцию, которая дает нам способ выполнять простые интерполяции без хлопот синтаксического анализа выражений.

Кроме того, Hibernate Validator предлагает ParameterMessageInterpolator, не основанный на выражениях, и поэтому нам не нужны дополнительные библиотеки для его настройки.

3. Настройка пользовательских интерполяторов сообщений

Чтобы удалить зависимость от языка выражений, мы можем использовать пользовательские интерполяторы сообщений и настроить Hibernate Validator без поддержки выражений.

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

3.1. Настройка ValidatorFactory

Одним из способов настройки пользовательского интерполятора сообщений является настройка ValidatorFactory при начальной загрузке.

Таким образом, мы можем построить экземпляр ValidatorFactory с ParameterMessageInterpolator:

ValidatorFactory validatorFactory = Validation.byDefaultProvider()
  .configure()
  .messageInterpolator(new ParameterMessageInterpolator())
  .buildValidatorFactory();

3.2. Настройка Validator

Точно так же мы можем установить ParameterMessageInterpolator при инициализации экземпляра Validator:

Validator validator = validatorFactory.usingContext()
  .messageInterpolator(new ParameterMessageInterpolator())
  .getValidator();

4. Выполнение проверок

Чтобы увидеть, как работает ParameterMessageInterpolator, нам нужен образец Java-бина с некоторыми аннотациями JSR 380 на нем. .

4.1. Пример Java-бина

Давайте определим наш образец Java-бина Person:

public class Person {

    @Size(min = 10, max = 100, message = "Name should be between {min} and {max} characters")
    private String name;

    @Min(value = 18, message = "Age should not be less than {value}")
    private int age;

    @Email(message = "Email address should be in a correct format: ${validatedValue}")
    private String email;

    // standard getters and setters
}

4.2. Тестирование параметров сообщения

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

Итак, нам нужно получить доступ к нашему валидатору:

Validator validator = validatorFactory.getValidator();

После этого мы можем написать наш тестовый метод для поля имени:

@Test
public void givenNameLengthLessThanMin_whenValidate_thenValidationFails() {
    Person person = new Person();
    person.setName("John Doe");
    person.setAge(18);

    Set<ConstraintViolation<Person>> violations = validator.validate(person);
 
    assertEquals(1, violations.size());

    ConstraintViolation<Person> violation = violations.iterator().next();
 
    assertEquals("Name should be between 10 and 100 characters", violation.getMessage());
}

Сообщение проверки интерполируется с переменными {min} и {max} правильно:

Name should be between 10 and 100 characters

Далее, давайте напишем аналогичный тест для поля age:

@Test
public void givenAgeIsLessThanMin_whenValidate_thenValidationFails() {
    Person person = new Person();
    person.setName("John Stephaner Doe");
    person.setAge(16);

    Set<ConstraintViolation<Person>> violations = validator.validate(person);
 
    assertEquals(1, violations.size());

    ConstraintViolation<Person> violation = violations.iterator().next();
 
    assertEquals("Age should not be less than 18", violation.getMessage());
}

Точно так же сообщение проверки корректно интерполируется с переменной {value}, как мы и ожидали:

Age should not be less than 18

4.3. Тестирование выражений

Чтобы увидеть, как ParameterMessageInterpolator ведет себя с выражениями, давайте напишем еще один тест для поля электронной почты, который включает простое выражение ${validatedValue}:

@Test
public void givenEmailIsMalformed_whenValidate_thenValidationFails() {
    Person person = new Person();
    person.setName("John Stephaner Doe");
    person.setAge(18);
    person.setEmail("johndoe.dev");
    
    Set<ConstraintViolation<Person>> violations = validator.validate(person);
 
    assertEquals(1, violations.size());
    
    ConstraintViolation<Person> violation = violations.iterator().next();
 
    assertEquals("Email address should be in a correct format: ${validatedValue}", violation.getMessage());
}

На этот раз выражение ${validatedValue} не интерполируется.

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

5. Заключение

В этой статье мы узнали, для чего нужен ParameterMessageInterpolator и как его настроить в Hibernate Validator.

Как всегда, все примеры из этого руководства доступны на GitHub.