Generic Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Overview

One of the features of Java JSR 380 is allowing expressions while interpolating the validation messages with parameters.

When we use Hibernate Validator, there is a requirement that we need to add one of the uniform implementations of Java JSR 341 as a dependency to our project. JSR 341 is also called the Expression Language API.

However, adding an extra library can be cumbersome if we don't need to support parsing expressions according to our use-case.

In this short tutorial, we'll have a look at how to configure ParameterMessageInterpolator in Hibernate Validator.

2. Message Interpolators

Beyond the basics of validating a Java bean, the Bean Validation API's MessageInterpolator is an abstraction that gives us a way of performing simple interpolations without the hassle of parsing expressions.

Besides, Hibernate Validator offers a non-expression based ParameterMessageInterpolator, and therefore, we don't need any extra libraries to configure it.

3. Setting up Custom Message Interpolators

To remove the expression language dependency, we can use custom message interpolators and configure Hibernate Validator without expression support.

Let's show some of the convenient ways of setting up custom message interpolators. We'll use the built-in ParameterMessageInterpolator in our case.

3.1. Configuring ValidatorFactory

One way of setting up a custom message interpolator is to configure ValidatorFactory when bootstrapping.

Thus, we can build a ValidatorFactory instance with ParameterMessageInterpolator:

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

3.2. Configuring Validator

Similarly, we can set ParameterMessageInterpolator when we initialize the Validator instance:

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

4. Performing Validations

To see how ParameterMessageInterpolator works, we need a sample Java bean with some JSR 380 annotations on it.

4.1. Sample Java Bean

Let's define our sample Java bean 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. Testing Message Parameters

Certainly, to perform our validations, we should use a Validator instance accessed from ValidatorFactory, which we already configured before.

So, we need to access our Validator:

Validator validator = validatorFactory.getValidator();

After that, we can write our test method for the name field:

@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());
}

The validation message is interpolated with variables of {min} and {max} correctly:

Name should be between 10 and 100 characters

Next, let's write a similar test for the age field:

@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());
}

Similarly, the validation message is interpolated correctly with the variable {value} as we expected:

Age should not be less than 18

4.3. Testing Expressions

To see how ParameterMessageInterpolator behaves with expressions, let's write another test for the email field that involves a simple ${validatedValue} expression:

@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());
}

This time, the expression ${validatedValue} is not interpolated.

ParameterMessageInterpolator only supports the interpolation of parameters, not parsing expressions that use the $ notation. Instead, it simply returns them un-interpolated.

5. Conclusion

In this article, we learned what ParameterMessageInterpolator is for and how to configure it in Hibernate Validator.

As always, all the examples involved in this tutorial are available over on GitHub.

Generic bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

Leave a Reply

avatar
  Subscribe  
Notify of