Java Spring 4 和 Hibernate 5 方法参数验证

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21696146/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 10:07:31  来源:igfitidea点击:

Spring 4 and Hibernate 5 method argument validation

javaspringhibernate-5.x

提问by Rinat Mukhamedgaliev

How validate incoming arguments with Hibernate ?

如何使用 Hibernate 验证传入参数?

In XML

在 XML 中

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
    <property name="validator" ref="validator"/>
</bean>

In Java

在 Java 中

 @Validated
 public class UserService

 @Override
 @NotNull
 public User registerUser(@NotEmpty String name, String username, String password, boolean google, boolean facebook)

This approach not work i call method with error params and validation not work.

这种方法不起作用我用错误参数调用方法并且验证不起作用。

采纳答案by asinkxcoswt

Question Clarification

问题澄清

When using Hibernate Validator (HV) 4.2 with Spring-MVC 4, e.g. with dependencies:

将 Hibernate Validator (HV) 4.2 与 Spring-MVC 4 一起使用时,例如具有依赖项:

dependencies {
    providedCompile 'javax.servlet:javax.servlet-api:3.0.1'
    compile 'org.springframework:spring-webmvc:4.0.3.RELEASE'
    compile 'org.hibernate:hibernate-validator:4.2.0.Final'
    runtime 'javax.servlet:jstl:1.1.2'
}

one can tell Spring to automatically use HV to validate method calls of classes annotated with @Validated(as in asker's example) by declaring following beans in the configuration file,

可以@Validated通过在配置文件中声明以下 bean来告诉 Spring 自动使用 HV 来验证使用注释的类的方法调用(如提问者的示例),

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>

When the target's method get called and has somethings that violates its constraints, the MethodConstraintViolationExceptionis thrown. For example, something like this will be raised if one happeded to call UserService#registerUser(...), passing an empty String to where annotated with @NotEmpty

当目标的方法被调用并且有一些违反其约束的东西时,MethodConstraintViolationException抛出。例如,如果有人碰巧调用UserService#registerUser(...),将一个空字符串传递到注释为@NotEmpty

... The following constraint violations occurred: [MethodConstraintViolationImpl
[method=public abstract void foo.UserService.registerUser(java.lang.String),
parameterIndex=0, parameterName=arg0, kind=PARAMETER, message=may not be empty, 
messageTemplate={org.hibernate.validator.constraints.NotEmpty.message}, ...

But the problem is if you change the dependencies from above to

但问题是,如果您将依赖项从上面更改为

dependecies {
    providedCompile 'javax.servlet:javax.servlet-api:3.0.1'
    compile 'org.springframework:spring-webmvc:4.0.3.RELEASE'
    compile 'org.hibernate:hibernate-validator:5.1.0.Final' //**note the change here**
    runtime 'javax.servlet:jstl:1.1.2'
}

then you will get an error:

那么你会得到一个错误:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name
'org.springframework.validation.beanvalidation.MethodValidationPostProcessor#0'
defined in class path resource [spring/config/web/main-config.xml]: Invocation of init
method failed; nested exception is javax.validation.ValidationException: Unable to
instantiate Configuration.

Solution

解决方案

Add 2 additional dependecies to the classpath as follows,

向类路径添加 2 个额外的依赖项,如下所示,

dependecies {
    providedCompile 'javax.servlet:javax.servlet-api:3.0.1'
    providedCompile 'javax.el:el-api:2.2'
    providedCompile 'org.glassfish.web:el-impl:2.2'
    compile 'org.springframework:spring-webmvc:4.0.3.RELEASE'
    compile 'org.hibernate:hibernate-validator:5.1.0.Final' //**note the change here**
    runtime 'javax.servlet:jstl:1.1.2'
}

And the validation will comeback to life. But this time, instead of MethodConstraintViolationException, it raises ConstranitViolationExceptionwith no details of what has been violated.

验证将恢复生机。但这一次,而不是MethodConstraintViolationException,它提出ConstranitViolationException了没有被违反的细节。

I am not sure how Spring manage this internally and wheter you can unfold the violation details out of this Exception. HV 5 no longer supports MethodConstraintViolationExceptionand its relatives, e.g. MethodValidator, MethodConstraintViolation. This is something to do with Bean Validation 1.1 specification, which has some changes in method-level validation from BV 1.0. I suggest creating the validation service yourself using aspect and with ExecutableValidator(instead of the old MethodValidator) as explained in HV's Reference guide.

我不确定 Spring 如何在内部管理它,以及您是否可以从这个异常中展开违规细节。HV 5 不再支持MethodConstraintViolationException及其亲属,例如MethodValidatorMethodConstraintViolation。这与Bean Validation 1.1 规范有关,它在方法级验证方面与 BV 1.0 有一些变化。我建议自己使用 aspect 和 with ExecutableValidator(而不是旧的MethodValidator)创建验证服务,如HV 的参考指南 中所述

Note that you if you try to obtain the validator like this,

请注意,如果您尝试像这样获取验证器,

ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
ExecutableValidator executableValidator = factory.getValidator().forExecutables();

you may get ValidationException: Unable to instantiate configuration.I can solve this problem by do this

你可能会知道ValidationException: Unable to instantiate configuration.我可以通过这样做来解决这个问题

ValidatorFactory factory = Validation.byProvider(HibernateValidator.class).configure().buildValidatorFactory();
ExecutableValidator executableValidator = factory.getValidator().forExecutables();

回答by Magnus Smith

I include the following in addition to spring and other project dependencies in the maven pom file.

除了 spring 和 maven pom 文件中的其他项目依赖项之外,我还包括以下内容。

<properties>
    <hibernate.version>4.3.1.Final</hibernate.version>
    <hibernate.validator.version>5.1.0.Final</hibernate.validator.version>
    <javax.el.version>3.0.0</javax.el.version>
    <asm.version>3.3.1</asm.version>
</properties>
<dependencies>
    <!-- Hibernate et al-->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>${hibernate.version}</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>${hibernate.version}</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>${hibernate.validator.version}</version>
    </dependency>
    <dependency>
        <groupId>javax.el</groupId>
        <artifactId>javax.el-api</artifactId>
        <version>${javax.el.version}</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.web</groupId>
        <artifactId>javax.el</artifactId>
        <version>${javax.el.version}</version>
    </dependency>
    <!-- asm allows hibernate validator to retrieve method parameter names when reporting method validation
        without this you just get arg0 instead of argument name -->
    <dependency>
        <groupId>asm</groupId>
        <artifactId>asm</artifactId>
        <version>${asm.version}</version>
    </dependency>
</dependencies>

In my java config for spring I set

在我为 spring 设置的 java 配置中

@Bean
public MessageSource messageSource() {
    ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
    messageSource.setBasename("validation");
    messageSource.setCacheSeconds(1);

    return messageSource;
}


@Bean
LocalValidatorFactoryBean getValidator() {
    LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
    validator.setValidationMessageSource(messageSource());
    validator.setParameterNameDiscoverer(new LocalVariableTableParameterNameDiscoverer());
    return validator;

}

@Bean
@Autowired
MethodValidationPostProcessor getValidationPostProcessor(LocalValidatorFactoryBean validator) {
    MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
    processor.setValidator(validator);
    return processor;
}

LocalVariableTableParameterNameDiscoverer works with ASM to give proper parameter names in the ConstraintViolationException that will be thrown when validation fails.

LocalVariableTableParameterNameDiscoverer 与 ASM 一起工作,在 ConstraintViolationException 中给出正确的参数名称,当验证失败时将抛出该异常。

回答by Serkan Kaba

Apparently Spring suggests not to use ExecutableValidator but MethodValidationInterceptor instead, see javadoc: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.html

显然 Spring 建议不要使用 ExecutableValidator 而是 MethodValidationInterceptor,请参阅 javadoc:http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.html

回答by KHanusova

For current version you don't have to add all beans that "asinkxcoswt" wrote in https://stackoverflow.com/a/23384229/6028739.

对于当前版本,您不必添加“asinkxcoswt”在https://stackoverflow.com/a/23384229/6028739 中写入的所有 bean 。

To your pom.xml add this:

在您的 pom.xml 中添加以下内容:

<dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-validator</artifactId>
   <version>5.3.4.Final</version>
</dependency>
<dependency>
   <groupId>org.glassfish.web</groupId>
   <artifactId>javax.el</artifactId>
   <version>2.2.4</version>
</dependency>

Add this line to your application context:

将此行添加到您的应用程序上下文中:

<bean id="validationFactory" class="javax.validation.Validation"  
      factory-method="buildDefaultValidatorFactory" />  

All entities annotated with @Validated are now validated and throw javax.validation.ConstraintViolationExceptionwith list of constraint violations with details e.g.

所有用@Validated 注释的实体现在都经过验证,并抛出javax.validation.ConstraintViolationException带有详细信息的约束违规列表,例如

javax.validation.ConstraintViolationException: Validation failed for classes [com.example.Class] during update time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
ConstraintViolationImpl{interpolatedMessage='must be greater than or equal to 0', propertyPath=amount, rootBeanClass=class com.example.Class, messageTemplate='{javax.validation.constraints.Min.message}'}
]