Spring Controller double validation for @ModelAttribute object

801 Views Asked by At

I have implemented controller with method:

public Page<EventDto> search(@ModelAttribute @Valid EventsCriteria criteria){...}

It works. EventsCriteria did not have special separate validator for itself, because it has inner annotation checkers like @NotNull @NotEmpty. Right now situation is changed and it's not enough to have only this validators. so I've added and registered correspond service to verify some sates.

I want service to verify additional composite cases and single validation like before. But my single inner annotation checkers like @NotNull @NotEmpty do not work any more.

Am I configured something incorrectly or it can't work in in same time(specified service and inner checkers)?

My validator example (as it was asked in comments)

There are nothing special, if you know spring, so I missed this part like unimportant to concentrate your attention on my question.

@Component
public class EventsCriteriaValidator implements Validator {

    public static final String INVALID_RAGE_PARAMETERS_SPECIFIED = "Invalid rage parameters: please specify " +
            "'from' and 'to' or page 'size";

    @Override
    public boolean supports(Class<?> clazz) {
        return clazz.isAssignableFrom(EventsCriteria.class);
    }

    @Override
    public void validate(Object target, Errors errors) {
        EventsCriteria toValidate = (EventsCriteria) target;
        validateComplexRestriction(toValidate, errors);
    }

    private void validateComplexRestriction(EventsCriteria toValidate, Errors errors) {
        if (toValidate.isRangeEmpty() && toValidate.getSize() == 0) {
            errors.rejectValue(SIZE_FIELD, null, INVALID_RAGE_PARAMETERS_SPECIFIED);
        }
    }

}
3

There are 3 best solutions below

1
On

Edited:

If you want both custom and default validation you can use addValidators in initBinder to add your custom validations. This way default validations will not be deleted.

@Controller
public class MyController
{
    @Autowired
    private EventsCriteriaValidator validator;

    @InitBinder
    protected void initBinder(final WebDataBinder binder)
    {
         binder.addValidators(validator);
    }

  public Page<EventDto> search(@ModelAttribute @Valid EventsCriteria   criteria, BindingResult result){

     if (result.hasErrors()) {
        // validation failed

     } else {
        //validation passed   
     }
  }

}
1
On

Just make your Validator extend LocalValidatorFactoryBean and then call super.validate(target, errors) in your validate method like this:

@Override
public void validate(Object target, Errors errors) {
   super.validate(target, errors);

   EventsCriteria toValidate = (EventsCriteria) target;
   validateComplexRestriction(toValidate, errors);
}
1
On

Let assume that your EventsCriteria contains two fields so inside your validate method you can validate those field using : ValidationUtils (org.springframework.validation.ValidationUtils)

 @Override
    public void validate(Object target, Errors errors) {
        EventsCriteria eventsCriteria= (EventsCriteria) target;
        ValidationUtils.rejectIfEmpty(errors, "field1", "eventsCriteria.field1.empty");
        ValidationUtils.rejectIfEmpty(errors, "field2", "eventsCriteria.field2.empty");
    }

and in your messages.properties

eventsCriteria.field1.empty = Enter a valid value for field 1.
eventsCriteria.field2.empty = Enter a valid value for field 2.

and in your configuration class add this bean:

 @Bean
   public MessageSource messageSource() {
      ResourceBundleMessageSource source = new ResourceBundleMessageSource();
      source.setBasename("messages");
      return source;
   }