Spring validation for RequestBody parameters bound to collections in Controller methods

后端 未结 3 603
遇见更好的自我
遇见更好的自我 2020-12-10 16:32

I have

An Entity:

package org.ibp.soq;

public class MyEntity {

    private String field1;
    private String field2;

    //..get         


        
相关标签:
3条回答
  • 2020-12-10 17:05

    Actually, this can be achieved using Spring Validation and JSR303.

    • Expose a MethodValidationPostProcessor bean.
    • Annotate your controller class with @Validated (org.springframework.validation.annotation.Validated)
    • Use the JSR303 validation annotations on your MyEntity fields/methods.
    • Annotate your RequestBody argument with @Valid (you've already done this in your example).
    • Add an @ExceptionHandler method to handle MethodArgumentNotValidException. This can be done in the controller or in a @ControllerAdvice class.
    0 讨论(0)
  • 2020-12-10 17:09

    The solution is to create a custom Validator for Collection and a @ControllerAdvice that registers that Validator in the WebDataBinders.

    Validator:

    import java.util.Collection;
    
    import org.springframework.validation.Errors;
    import org.springframework.validation.ValidationUtils;
    import org.springframework.validation.Validator;
    import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
    
    /**
     * Spring {@link Validator} that iterates over the elements of a 
     * {@link Collection} and run the validation process for each of them
     * individually.
     *   
     * @author DISID CORPORATION S.L. (www.disid.com)
     */
    public class CollectionValidator implements Validator {
    
      private final Validator validator;
    
      public CollectionValidator(LocalValidatorFactoryBean validatorFactory) {
        this.validator = validatorFactory;
      }
    
      @Override
      public boolean supports(Class<?> clazz) {
        return Collection.class.isAssignableFrom(clazz);
      }
    
      /**
       * Validate each element inside the supplied {@link Collection}.
       * 
       * The supplied errors instance is used to report the validation errors.
       * 
       * @param target the collection that is to be validated
       * @param errors contextual state about the validation process
       */
      @Override
      @SuppressWarnings("rawtypes")
      public void validate(Object target, Errors errors) {
        Collection collection = (Collection) target;
        for (Object object : collection) {
          ValidationUtils.invokeValidator(validator, object, errors);
        }
      }
    }
    

    ControllerAdvice:

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
    import org.springframework.web.bind.WebDataBinder;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.InitBinder;
    
    /**
     * Controller advice that adds the {@link CollectionValidator} to the 
     * {@link WebDataBinder}.
     * 
     * @author DISID CORPORATION S.L. (www.disid.com)
     */
    @ControllerAdvice
    public class ValidatorAdvice {
    
      @Autowired
      protected LocalValidatorFactoryBean validator;
    
    
      /**
       * Adds the {@link CollectionValidator} to the supplied 
       * {@link WebDataBinder}
       * 
       * @param binder web data binder.
       */
      @InitBinder
      public void initBinder(WebDataBinder binder) {
        binder.addValidators(new CollectionValidator(validator));
      }
    }
    
    0 讨论(0)
  • 2020-12-10 17:13

    As you might have guessed this cannot be achieved using Spring Validation. Spring Validation implements Bean Validation(JSR 303/349) as opposed to Object validation. Unfortunately a collection is not a Java Bean. You have two options

    • Wrap your list inside a Java Bean
    • Call the validator manually in your bulk create method myEntityValidator. validate(targetObject, errors).
    0 讨论(0)
提交回复
热议问题