One design pattern I’ve seen in professional projects is based on the fact,
that Bean Validation is not only integrated with JAX/RS, but also with CDI.
For the range sample the implementation of this pattern requires the definition
of a wrapper class around the Range
object, that implements the additional
validation as JSR 380 rule:
public class ValidateableRange {
private final Range range;
public ValidateableRange(Range range) {
this.range = range;
}
@Valid
public Range getRange() {
return range;
}
@AssertTrue(message = "min must be less than or equal to max")
public boolean isMinLessThanOrEqualToMax() {
return range.getMin() <= range.getMax();
}
}
In addition, a validator CDI bean is required:
@ApplicationScoped
public class ApplicationValidator {
public void validate(@Valid ValidateableRange validateableRange) {
// no-op
}
}
Please note, that the validator can contain several validation methods for
different objects, that need to be validated.
Please also note, that the validation method is empty. The @Valid
annotation
attached to the method argument is the crucial piece, that triggers the Bean
Validation when the method is called.
Given these classes, the validation can be triggered before the actual
processing of the request is started:
@Inject ApplicationValidator appValidator;
public Response newRange(Range range) {
appValidator.validate(new ValidateableRange(range));
// business logic goes here
}
The implementation of this pattern satisfies all requirements, but the 3rd one:
Request validation is performed two times, first when the request enters
the JAX/RS API method and second when the ApplicationValidator
enters the
stage. Because the ApplicationValidator
performs additional checks, invalid
requests fixed by the user may report more violations when called the second
time.
There are more problems with this pattern:
-
Some validation may be checked twice. That can become problematic when
checking the validation rules is rather expensive in terms of resource
consumption/performance.
-
The reported objects may be of type ValidateableRange
, although, the API
user requested a Range
object.
However, for many projects, in particular for internal APIs, the
behavior implemented by this CDI based validation pattern is sufficient.
In addition, it’s entirely based on Java code and therefor very flexible.
But if you offer a public REST API, the requirements of API consistency and
quality may be higher, also in terms of request validation.