使用BindingResult的Spring 4 @Validation注释无法正常工作

时间:2017-03-17 08:45:51

标签: java spring spring-validator

当我在方法中使用带有BindingResult注释的@Validated时,验证无效。如果我从方法参数中删除BindingResult,它可以正常工作。我用了@ControllerAdvice

知道为什么它不起作用?

我的代码如下:

public ResponseEntity<?> dologin(
        @Validated({ Group.Login.class }) @RequestBody User user,
        BindingResult errors)
{
    // somecode
}

根据Validation, Data Binding, and Type Conversion documentation, 任何验证消息都将自动添加到活页夹的BindingResult

如果我删除它会没有影响,对吗?

2 个答案:

答案 0 :(得分:1)

由于您只提供了控制器方法的代码,因此我添加了一些其他信息,以便在Spring 4应用程序中进行验证,因此您可以看到可能存在的差异。我最低限度地假设以下内容:

  1. 在您的类路径中包含bean-validation-api JAR。这会给你注释。
  2. 在类路径中包含Bean Validation实现JAR(例如,参考实现,hibernate-validator)。这启用了验证框架。
  3. 您的请求映射可以是以下形式之一:
  4. @RequestMapping(method = RequestMethod.POST)
    public String dologin(@Valid @ModelAttribute("userForm") User user, final BindingResult bindingResult) throws Exception
    
    @RequestMapping(method = RequestMethod.POST)
    public String dologin(@Validated @ModelAttribute("userForm") User user, final BindingResult bindingResult) throws Exception
    

    或者,如果使用validation groups

    @RequestMapping(method = RequestMethod.POST)
    public String dologin(@Validated({ User.Login.class }) @ModelAttribute("userForm") User user, final BindingResult bindingResult) throws Exception
    
    1. User类中的注释应与消息属性键内联:
    2. @NotBlank(message = "{firstName.required}")
      @Size(min = 2, max = 24, message = "{firstName.size}")
      @Pattern(regexp = "^[’' \\.\\-\\p{L}\\p{M}]*$", message = "{firstName.format}")
      private String firstName;
      

      或者,如果使用validation groups

      public interface Login extends Default {}
      
      @NotBlank(message = "{firstName.required}", groups = {Login.class})
      @Size(min = 2, max = 24, message = "{firstName.size}", groups = {Login.class})
      @Pattern(regexp = "^[’' \\.\\-\\p{L}\\p{M}]*$", message = "{firstName.format}", groups = {Login.class})
      private String firstName;
      
      1. 您的邮件属性应位于src/main/resources
      2. firstName.required=First name is required.
        firstName.size=First name must be between {min} and {max} characters long.
        firstName.format=First name may contain only letters, apostrophes, spaces and hyphens.
        

        确保您的Bean Validation specification版本符合您的实施版本。 Bean Validation API 2.0于2017年8月发布.Bean Validation 2.0参考实现是Hibernate Validation Engine 6.0

        如果方法参数中存在验证错误,则会将{}添加到BindingResult。或者,您可以使用Errors代替BindingResult - API基本相同。这些都在我上面的实现中等效地工作。

        但是,如果我将参数从我的控制器方法中删除,我会看到(从日志记录中)触发验证并引发相应的错误并将消息键映射到其属性成功,但是我的服务器错误页面被渲染而不是我期望的视图。有关此参数要求的其他信息,请参阅BindingResult/Errors parameter上的相关问题。

        Aug 31, 2017 2:21:56 PM com.test.spring.controller.ErrorController handleException
        SEVERE: Server Error
        org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 2 errors
        Field error in object 'userForm' on field 'firstName': rejected value []; codes [Size.userForm.firstName,Size.firstName,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userForm.firstName,firstName]; arguments []; default message [firstName],24,2]; default message [First name must be between 2 and 24 characters long.]
        Field error in object 'userForm' on field 'firstName': rejected value []; codes [NotBlank.userForm.firstName,NotBlank.firstName,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userForm.firstName,firstName]; arguments []; default message [firstName]]; default message [First name is required.]
            at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:117)
        

        我在上述所有组合中都看到了非常一致的行为。

        我注意到的一个区别是您在示例中使用@ControllerAdvice。我的大多数控制器使用@Controller注释(本地控制器错误),而不是@ControllerAdvice(用于全局错误,例如用于呈现HTTP 404或500错误页面)。有关@ControllerAdvice的使用,请参阅此相关问题/答案。

        在上面的错误日志中,如果我省略了ErrorController / BindingResult参数,您会看到Errors是报告验证错误的那个,这确实是我的控制器类使用@ControllerAdvice注释而不是@Controller。在登录控制器上使用@ControllerAdvice可能会干扰正常的错误处理。

        由于您使用的是Hibernate-specific @Validated组验证注释,而不是JSR-303/349/380规范@Valid注释,因此您使用的实现之间可能存在一些差异(I'猜测版本5,符合API 1.1)。我使用的是6.0.2版,符合新版(2017年8月)Bean Validation Framework API 2.0

答案 1 :(得分:1)

Hibernate Validator 6 不能与 Spring 4 一起开箱即用,因为 Spring 4 的 LocalValidatorFactoryBean 还没有使用 Java 的 Service Provider 机制来发现 Validator 实现,因此您需要自己创建验证器,指定实施。

使用 @EnableWebMvc 时:

@Bean
public javax.validation.Validator localValidatorFactoryBean() {
    LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
    bean.setProviderClass("org.hibernate.validator.HibernateValidator");
    return bean;
}

或在 XML 中:

<mvc:annotation-driven validator="validator-adapter"/>

<bean id="validator-adapter" 
  class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="providerClass"
              value="org.hibernate.validator.HibernateValidator" />
</bean>