fashuser
fashuser

Reputation: 1479

@Valid with spring annotations

I've enabled spring mvc annotation driven for my project. The idea is to use @Valid annotation with spring annotations to avoid the line in the controller like: validator.validate(form, errors)

I've noticed that this stuff does not work with spring annotations from package:

org.springmodules.validation.bean.conf.loader.annotation.handle

After investigation I've found that I can use annotations from javax or org.hibernate.validator.constraints as alternative way.

But unfortunatly, I have some special cases when I cannot achieve this:

@MinSize(applyIf = "name NOT EQUALS 'default'", value = 1)

Will be good to know in which way spring annotation can be used with @Valid or any other alternative way to avoid refactoring related to applyIf attributes(Move conditions to java code).

Upvotes: 1

Views: 2267

Answers (2)

devops
devops

Reputation: 9179

Here is an example how to create a custom Validator.

At first create your own Annotation:

@Target({ ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = StringValidator.class)
public @interface ValidString {

    String message() default "Invalid data";
    int min() default -1;
    int max() default -1;
    String regex() default "";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

Then you will need a custom validator:

public class StringValidator implements ConstraintValidator<ValidString, String> {

    private int              _min;
    private int              _max;
    private String           _regex;
    private boolean          _decode;

    public void initialize(ValidString constraintAnnotation) {
        _min = constraintAnnotation.min();
        _max = constraintAnnotation.max();
        _regex = constraintAnnotation.regex();
        _decode = constraintAnnotation.decode();
    }

    public boolean isValid(String value, ConstraintValidatorContext context) {

        if (value == null) {
            return false;
        }

        String test = value.trim();

        if (_min >= 0) {
            if (test.length() < _min) {
                return false;
            }
        }

        if (_max > 0) {
            if (test.length() > _max) {
                return false;
            }
        }

        if (_regex != null && !_regex.isEmpty()) {
            if (!test.matches(_regex)) {
                return false;
            }
        }

        return true;
    }
}

Finally you can use it in your Beans and Controller:

public class UserForm {

    @ValidString(min=4, max=20, regex="^[a-z0-9]+")
    private String name;

    //...
}

// Method from Controller
@RequestMapping(method = RequestMethod.POST)
public String saveUser(@Valid UserForm form, BindingResult brResult) {

    if (brResult.hasErrors()) {
        //TODO:
    }

    return "somepage";
}

Upvotes: 1

Ankur Singhal
Ankur Singhal

Reputation: 26067

Something like this might help you out

public class UserValidator implements Validator {

    @Override
    public boolean supports(Class clazz) {
      return User.class.equals(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
      User user = (User) target;

      if(user.getName() == null) {
          errors.rejectValue("name", "your_error_code");
      }

      // do "complex" validation here

    }

}

Then in your controller you would have :

@RequestMapping(value="/user", method=RequestMethod.POST)
    public createUser(Model model, @ModelAttribute("user") User user, BindingResult result){
        UserValidator userValidator = new UserValidator();
        userValidator.validate(user, result);

        if (result.hasErrors()){
          // do something
        }
        else {
          // do something else
        }
}

If there are validation errors, result.hasErrors() will be true.

Note : You can also set the validator in a @InitBinder method of the controller, with "binder.setValidator(...)" . Or you could instantiate it in the default constructor of the controller. Or have a @Component/@Service UserValidator that you inject (@Autowired) in your controller : very useful, because most validators are singletons + unit test mocking becomes easier + your validator could call other Spring components.

Upvotes: 0

Related Questions