【问题标题】:Spring MVC validator annotation + custom validationSpring MVC 验证器注解 + 自定义验证
【发布时间】:2014-09-24 09:19:51
【问题描述】:

我正在开发 spring mvc 应用程序,我应该基于 Spring MVC 验证器应用验证。我的第一步是为类和设置控制器添加注释,它工作正常。现在我需要实现自定义验证器来执行复杂的逻辑,但我想使用现有的注释并添加额外的检查。

我的用户类:

public class User
{
    @NotEmpty
    private String name;

    @NotEmpty
    private String login; // should be unique
}

我的验证者:

@Component
public class UserValidator implements Validator
{

    @Autowired
    private UserDAO userDAO;

    @Override
    public boolean supports(Class<?> clazz)
    {
        return User.class.equals(clazz) || UsersForm.class.equals(clazz);
    }

    @Override
    public void validate(Object target, Errors errors)
    {
        /*
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "NotEmpty.user");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "login", "NotEmpty.user");
        */
        User user = (User) target;
        if (userDAO.getUserByLogin(user.getLogin()) != null) {
            errors.rejectValue("login", "NonUniq.user");
        }
    }
}

我的控制器:

@Controller
public class UserController
{
    @Autowired
    private UserValidator validator;

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

    @RequestMapping(value = "/save")
    public ModelAndView save(@Valid @ModelAttribute("user") final User user,
            BindingResult result) throws Exception
    {
        if (result.hasErrors())
        {
            // handle error
        } else
        {
            //save user
        }
    }
}

那么,是否可以同时使用自定义验证器和注释?如果是的话怎么做?

【问题讨论】:

  • 您尝试做的事情是完全合法的。什么不工作?

标签: java spring validation spring-mvc


【解决方案1】:

对我来说你必须删除

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

离开

@Valid @ModelAttribute("user") final User user,
        BindingResult result

在函数make之后

validator.validate(user,result)

这样您将使用@Valid 的基本验证,然后您将进行更复杂的验证。

因为使用 initBinder,您可以使用复杂的逻辑设置验证,并为基本逻辑铺平道路。

也许是错的,我总是使用 @Valid 而没有任何验证器。

【讨论】:

  • 但是,例如,您如何检查该登录名的唯一性?
  • 然后检查一下,是的,如果传入方法 erorr 而不是 result,这是有效的。作为实现Error 我使用BindException docs.spring.io/spring/docs/3.1.x/javadoc-api/org/…
  • 嗯,我认为@serge-dallesta 的答案更好地解释并具有良好的逻辑,但这就是我想说的:)
  • 您不必在处理程序方法中进行显式验证调用,只是不要在 InitBinder 中设置验证器,而是添加它(参见下面@destan 的答案)
【解决方案2】:

如果我正确理解您的问题,一旦您使用自定义验证器,@NotEmpty 注释的默认验证将不再发生。这在使用 spring 时很常见:如果您覆盖默认给出的功能,则必须显式调用它。

您必须生成一个LocalValidatorFactoryBean 并将其与您的消息源(如果有)一起注入。然后在自定义验证器中注入该基本验证器并将注释验证委托给它。

使用 java 配置它可能看起来像:

@Configuration
public class ValidatorConfig {
    @Autowired
    private MessageSource messageSource;

    @Bean
    public Validator basicValidator() {
        LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
        validator.setValidationMessageSource(messageSource);
        return validator;
    }
}

然后你修改UserValidator来使用它:

@Component
public class UserValidator implements Validator
{

    @Autowired
    @Qualifier("basicValidator")
    private Validator basicValidator;

    @Autowired
    private UserDAO userDAO;

    // ...

    @Override
    public void validate(Object target, Errors errors)
    {
        basicValidator.validate(target, errors);
        // eventually stop if any errors
        //  if (errors.hasErrors()) { return; }
        User user = (User) target;
        if (userDAO.getUserByLogin(user.getLogin()) != null) {
            errors.rejectValue("login", "NonUniq.user");
        }
    }
}

【讨论】:

  • 感谢您的回答。我想有两个解决方案:你的和@nicearma。我不确定哪个更正确,但两者都有效。
  • @Vartlok 我的回答是通用的,因为存在用于发布值的全局验证(多参数)的用例。但我会尽量避免在控制器层的验证中访问数据库——除非你没有服务层。如果你有,这个验证是一个业务规则,应该去那里。
  • 但是如果errors.hasErrors中有一些错误,我们如何才能在UI上正确显示呢??
【解决方案3】:

我知道这是一个老问题,但是对于谷歌人来说......

您应该使用addValidators 而不是setValidator。如下:

@InitBinder
protected void initBinder(final WebDataBinder binder) {
    binder.addValidators(yourCustomValidator, anotherValidatorOfYours);
}

PS:addValidators 接受多个参数(省略号)

如果您查看org.springframework.validation.DataBinder 的来源,您将看到:

public class DataBinder implements PropertyEditorRegistry, TypeConverter {

    ....

    public void setValidator(Validator validator) {
        assertValidators(validator);
        this.validators.clear();
        this.validators.add(validator);
    }

    public void addValidators(Validator... validators) {
        assertValidators(validators);
        this.validators.addAll(Arrays.asList(validators));
    }

    ....

}

如您所见,setValidator 会清除现有(默认)验证器,因此 @Valid 注释将无法按预期工作。

【讨论】:

  • 这是的答案。谢谢!没有注意到 addValidators 方法
猜你喜欢
  • 2011-06-13
  • 2011-04-08
  • 1970-01-01
  • 2013-08-27
  • 2011-12-21
  • 2019-04-21
  • 1970-01-01
  • 1970-01-01
  • 2018-10-25
相关资源
最近更新 更多