Spring Boot conditional validation example.
Validating user entered data on the frontend is optional, although on the backend is a must. Spring Boot offers a wide range of validation annotations to use, but not out of the box solution for conditional or cross field validation. For this job we can create our own custom annotation, with the following stack :
- Spring Boot(1.4.2)
- Maven (3.3.9)
- IntelliJ IDEA
- Java 8
- Lombok library
- Apache Commons BeanUtils API
Let’s suppose we have the following java class ExportLocationDTO,
which as it’s name indicates is a Data Transfer Object. The main purpose of this class is to transfer the data between the user interface and the database. The locationOption
field’s type is ExportLocationOption
which is just an Enum with three possible values. A name
attribute is associated with each entry to make later validation more generic.
The name
field is a mandatory and annotated accordingly as well as the ExportLocationOption
.
The custom validation logic comes here:
- if
locationOption’s
value isEMAIL
theemailAddress
field is mandatory - if
locationOption's
value is eitherFTP
orSFTP
thehost
,port
,user
andpassword
fields are required.
This task can be achieved by a custom class level annotation.
With @Repeatable
we can repeat and stack more annotation on the the same class and it is governed by another simple annotation, Conditionals
:
Using this validator works as the following:
The actual validation logic is done by the ConditionalValidator
class.
In the initialize
method we assign the variables given in our annotation.
The isValid
method contains the concrete validation logic.
Object actualValue = BeanUtils.getProperty(object, selected);
With the help of some reflection and apache’sBeanUtils
library, store the validated object’s selected
field (in our case this is the locationOption
)
in actualValue
.
if (Arrays.asList(values).contains(actualValue))
If actualValue
is present in the values
array of the annotation
then by iterating through the required
array using reflection again we assign and check if the field’s value is not null or not empty.
for (String propName : required) {
Object requiredValue = BeanUtils.getProperty(objectToValidate, propName);
valid = requiredValue != null && !isEmpty(requiredValue);
Another important part is , since this a class level annotation used for field level validation the default constraint violation has to be overridden in order add a custom one. With this we can register a new ConstraintViolation with
a message and field associated with.
if (!valid) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(message)
.addPropertyNode(propName).addConstraintViolation();
}
And finally let’s see some test cases.
The full project can be found here: https://github.com/balag3/ConditionalValidator