Additional to the field-specific(Single Parameter Constraint) you can implement constraint for the whole method(Cross-Parameter Constraint). This will provide ability to pass all parameters of certain method to validator.
Annotation definition:
Annotation used two validators and can be applied to the Method or Type.
@Constraint(validatedBy = {ValidRequestMethodValidator.class, ValidRequestTypeValidator.class})
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidRequest {
String message() default "Request is invalid!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
ConstraintTarget validationAppliesTo() default ConstraintTarget.IMPLICIT;
}
Constraint Validator which will handle single parameter:
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class ValidRequestTypeValidator implements ConstraintValidator<ValidRequest, EventRequest> {
@Override
public boolean isValid(EventRequest request, ConstraintValidatorContext constraintValidatorContext) {
// logic here
return false;
}
}
Constraint Validator which will handle all parameters of specific method:
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.constraintvalidation.SupportedValidationTarget;
import javax.validation.constraintvalidation.ValidationTarget;
@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class ValidRequestMethodValidator implements ConstraintValidator<ValidRequest, Object[]> {
@Override
public boolean isValid(Object[] objects, ConstraintValidatorContext constraintValidatorContext) {
Long entityId = null;
EventRequest eventRequest = null;
if (objects[0] instanceof Long) {
entityId = (Long) objects[0];
}
if (objects[0] instanceof EventRequest) {
eventRequest = (EventRequest) objects[0];
}
if (objects[1] instanceof EventRequest) {
eventRequest = (EventRequest) objects[1];
}
//logic here
return false;
}
}
Please note, we have to annotate the beans, which shall be validated, with @org.springframework.validation.annotation.Validated annotation to get method validators to work automatically.
Example of usage:
- Mixed usage,
@ValidRequest annotation defined on method and single parameter level.
@ValidRequest
public class EventRequest {
public String value;
}
@RestController
@Validated
public class Controller {
Response create(@Valid EventRequest request) {
return new Response();
}
@ValidRequest(validationAppliesTo = ConstraintTarget.PARAMETERS)
Response update(Long entityId, EventRequest request) {
return new Response();
}
}
For create method ValidRequestTypeValidator will be executed.
For update method ValidRequestMethodValidator will be executed.
2. Define annotation only for methods
@RestController
@Validated
public class Controller {
@ValidRequest(validationAppliesTo = ConstraintTarget.PARAMETERS)
Response create(EventRequest request) {
return new Response();
}
@ValidRequest(validationAppliesTo = ConstraintTarget.PARAMETERS)
Response update(Long entityId, EventRequest request) {
return new Response();
}
}
For create method ValidRequestMethodValidator will be executed with one element objects array
For update method ValidRequestMethodValidator will be executed with two elements objects array
3. Define annotation for a single parameter and method at the same time
@ValidRequest
public class EventRequest {
public String value;
}
@RestController
@Validated
public class Controller {
@ValidRequest(validationAppliesTo = ConstraintTarget.PARAMETERS)
Response update(Long entityId, @Valid EventRequest request) {
return new Response();
}
}
First will be executed single parameter validator ValidRequestTypeValidator.
If it will passed validation then second method validator ValidRequestMethodValidator will be executed.
Probably only one method-level validation will be sufficient to handle your issue. I described all variants, just for information maybe will be useful.