Ste*_*sen 5 validation spring-mvc bean-validation spring-boot
我有一个用于输入日期的Spring MVC表单,输入被发送到Controller并通过标准Spring MVC验证进行了验证。
模型:
public class InvoiceForm {
@Future
private LocalDate invoicedate;
}
Run Code Online (Sandbox Code Playgroud)
控制器:
public String postAdd(@Valid @ModelAttribute InvoiceForm invoiceForm, BindingResult result) {
....
}
Run Code Online (Sandbox Code Playgroud)
提交表单时,出现以下错误:
public class InvoiceForm {
@Future
private LocalDate invoicedate;
}
Run Code Online (Sandbox Code Playgroud)
我ConstraintValidator为这种情况实施了自己的程序。但是以某种方式,Spring Boot不会选择它进行验证。
@Component
public class LocalDateFutureValidator implements ConstraintValidator<Future, LocalDate> {
@Override
public void initialize(Future future) {
}
@Override
public boolean isValid(LocalDate localDate, ConstraintValidatorContext constraintValidatorContext) {
LocalDate today = LocalDate.now();
return localDate.isEqual(today) || localDate.isAfter(today);
}
}
Run Code Online (Sandbox Code Playgroud)
我知道我可以简单地编写自己的注释并在其中指定验证器,但是不是更干净,更简单的方法吗?
我同意Miloš的观点,即使用META-INF/validation.xml可能是最干净,最简单的方法,但是如果您确实想在Spring @Confguration类中进行设置,则可以这样做,这是一种方法。
Spring Boot的优点是可以代表您进行大量配置,因此您不必担心。但是,当您要自己专门配置某些东西并且操作方法不是很明显时,这也会引起问题。
因此,昨天我着手尝试添加CustomerValidatorfor @Past并LocalDate使用ConstraintDefinitionContributorHardy建议的机制(在Hibernate文档中进行了引用)。
简单的一点是编写实现类进行验证,出于我的高度特定的目的,该验证类包括:
public class PastValidator implements ConstraintValidator<Past, LocalDate> {
@Override
public void initialize(Past constraintAnnotation) {}
@Override
public boolean isValid(LocalDate value, ConstraintValidatorContext context) {
return null != value && value.isBefore(LocalDate.now());
}
}
Run Code Online (Sandbox Code Playgroud)
然后我变得很懒,只是@Bean在我的配置中实例化了一个,只是偶然的是,Spring的自动配置类之一会选择它并将其连接到Hibernate验证器中。考虑到可用的文档(或缺少文档)以及Hardy和其他人所说的话,这是一个很长的路要走,但没有回报。
因此,我启动了一个调试器,并从抛出的异常向后进行了工作,该异常org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree告诉我无法为@Pastand 查找验证器LocalDate。
综观的类型层次ConstraintValidatorFactory我发现有在我的Spring MVC应用双机实现类:SpringConstraintValidatorFactory与SpringWebConstraintValidatorFactory这两者只是尝试,并从正确的类的情况下得到的bean。这告诉我,我必须在Spring的上注册我的验证器BeanFactory,但是当我在该点上插入一个断点但没有被击中时PastValidator,这意味着Hibernate甚至不知道它甚至应该请求此类。
这是有道理的:没有ConstraintDefinitionContributor地方可以告诉Hibernate,它需要向Spring请求PastValidator的实例。在文档中的例子
http://docs.jboss.org/hibernate/validator/5.2/reference/en-US/html_single/#section-constraint-definition-contributor
表明,我需要访问HibernateValidatorConfiguration,所以我只是需要找到Spring在哪里进行配置。
经过一番挖掘,我发现这一切都是在Spring的LocalValidatorFactoryBean类中发生的,特别是在其afterPropertiesSet()方法中。从其javadoc:
/*
* This is the central class for {@code javax.validation} (JSR-303) setup in a Spring
* application context: It bootstraps a {@code javax.validation.ValidationFactory} and
* exposes it through the Spring {@link org.springframework.validation.Validator} interface
* as well as through the JSR-303 {@link javax.validation.Validator} interface and the
* {@link javax.validation.ValidatorFactory} interface itself.
*/
Run Code Online (Sandbox Code Playgroud)
基本上,如果您没有设置和配置自己的Validator,那么Spring就是在这里为您准备的,而在真正的Spring风格中,它提供了一种方便的扩展方法,以便您可以对其进行配置,然后添加您的自己混合。
因此,我的解决方案是扩展,LocalValidatorFactoryBean以便能够注册自己的ConstraintDefinitionContributor实例:
import java.util.ArrayList;
import java.util.List;
import javax.validation.Configuration;
import org.hibernate.validator.internal.engine.ConfigurationImpl;
import org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
public class ConstraintContributingValidatorFactoryBean extends LocalValidatorFactoryBean {
private List<ConstraintDefinitionContributor> contributors = new ArrayList<>();
public void addConstraintDefinitionContributor(ConstraintDefinitionContributor contributor) {
contributors.add(contributor);
}
@Override
protected void postProcessConfiguration(Configuration<?> configuration) {
if(configuration instanceof ConfigurationImpl) {
ConfigurationImpl config = ConfigurationImpl.class.cast(configuration);
for(ConstraintDefinitionContributor contributor : contributors)
config.addConstraintDefinitionContributor(contributor);
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后实例化并在Spring配置中进行配置:
@Bean
public ConstraintContributingValidatorFactoryBean validatorFactory() {
ConstraintContributingValidatorFactoryBean validatorFactory = new ConstraintContributingValidatorFactoryBean();
validatorFactory.addConstraintDefinitionContributor(new ConstraintDefinitionContributor() {
@Override
public void collectConstraintDefinitions(ConstraintDefinitionBuilder builder) {
builder.constraint( Past.class )
.includeExistingValidators( true )
.validatedBy( PastValidator.class );
}
});
return validatorFactory;
}
Run Code Online (Sandbox Code Playgroud)
为了完整起见,这也是我用PastValidatorbean 实例化的地方:
@Bean
public PastValidator pastValidator() {
return new PastValidator();
}
Run Code Online (Sandbox Code Playgroud)
其他弹力小东西
我在调试中注意到,由于我有一个很大的Spring MVC应用程序,因此我看到了的两个实例SpringConstraintValidatorFactory和的一个实例SpringWebConstraintValidatorFactory。我发现后者在验证期间从未使用过,因此我暂时将其忽略。
Spring还具有一种决定使用哪种实现的机制ValidatorFactory,因此它有可能不使用您的ConstraintContributingValidatorFactoryBean而是使用其他东西(对不起,我昨天找到了它在其中使用的类,但今天找不到它,尽管我只花了大约2分钟的时间)。如果您以任何不平凡的方式使用Spring MVC,那么您可能已经必须编写自己的配置类,例如该类,该类实现了WebMvcConfigurer可以显式连接Validatorbean的地方:
public static class MvcConfigurer implements WebMvcConfigurer {
@Autowired
private ConstraintContributingValidatorFactoryBean validatorFactory;
@Override
public Validator getValidator() {
return validatorFactory;
}
// ...
// <snip>lots of other overridden methods</snip>
// ...
}
Run Code Online (Sandbox Code Playgroud)
这是错的
正如已经指出的那样,您应该警惕对进行@Past验证,LocalDate因为没有时区信息。但是,如果您LocalDate因为所有内容都将在同一时区运行而使用,或者您故意忽略时区,或者只是不在乎,那么这对您来说很好。
您需要将自己的验证器添加到META-INF/validation.xml文件中,如下所示:
<constraint-mappings
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.1.xsd"
xmlns="http://jboss.org/xml/ns/javax/validation/mapping" version="1.1">
<constraint-definition annotation="javax.validation.constraints.Future">
<validated-by include-existing-validators="true">
<value>package.to.LocalDateFutureValidator</value>
</validated-by>
</constraint-definition>
</constraint-mappings>
Run Code Online (Sandbox Code Playgroud)
更多详细信息,请参阅官方文档。
| 归档时间: |
|
| 查看次数: |
3497 次 |
| 最近记录: |