Search code examples
javaspringjavax.validation

Validate only annotated parameters in javax.validation.ConstraintValidator


I have a ConstraintValidator that needs to validate the input parameter (periodDays) depending on another parameter (id). To do this, I implemented validation logic based on ElementType.METHOD. My interface:

@Target({ElementType.METHOD, ElementType.TYPE})
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = { DaysPeriodValidator.class })
public @interface ValidDaysPeriod {
    // ...

and Validator:

@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class DaysPeriodValidator implements ConstraintValidator<ValidDaysPeriod, Object[]> {

    @Override
    public boolean isValid(Object[] objects, ConstraintValidatorContext context) {
        Integer testValue;
        String id;
        try {
            testValue = (Integer) objects[1];
            id = validateTenantId((String) objects[0]);
        } catch (Exception e) {
            return false;
        }
       // ... validation logic
        return valid;
    }
}

It works well for controller methods like

    @ValidDaysPeriod
    @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public MyData getMyData(@Nullable String id,
                            @NotNull Integer periodDays) {

where parameters order is expected. But I'm not immune if somebody would write a new endpoint where parameters are not in this order. The Validator gets them in nameless Object[] objects, and if there are more Integer or String parameters, I can't recognize id and periodDays among them.

Is there a way to detect these parameters inside the Validator? For example:

  • add specific annotations to them
  • pass parameter names with values to the Object[] objects?

Solution

  • Eventually, I found an ability to get parameter names from the context. Their order is strictly the same as for values in Objects[].

    public boolean isValid(Object[] objects, ConstraintValidatorContext context) {
            Integer testValue;
            String id;
    
            try {
                List<String> methodParameterNames = ((CrossParameterConstraintValidatorContextImpl) context).getMethodParameterNames();
                int idIdndex = methodParameterNames.indexOf(idParameterName);
                int periodDaysIndex = methodParameterNames.indexOf(periodDaysParameterName);
                testValue = (Integer) objects[periodDaysIndex];
                id = validateTenantId((String) objects[idIndex]);
            } catch (Exception e) {
                return false;
            }
            // validator logic
    }
    

    P.S. Guy who dislikes w/o comments, you can f*ck yourself:)