【问题标题】:JSR-303 validated property does not have a corresponding accessor for Spring data bindingJSR-303 验证属性没有对应的 Spring 数据绑定访问器
【发布时间】:2012-11-09 14:01:14
【问题描述】:

0.4 和 hibernate 验证器 4.1.0.Final,我创建了一个自定义验证器来验证密码和确认密码字段,但是验证器抛出了以下异常:

00:16:11,891 DEBUG [org.hibernate.validator.resourceloading.PlatformResourceBundleLocator] (http--192.168.1.20-8080-6) ValidationMessages not found.
00:16:11,892 DEBUG [org.hibernate.validator.resourceloading.PlatformResourceBundleLocator] (http--192.168.1.20-8080-6) org.hibernate.validator.ValidationMessages found
00:16:11,894 DEBUG [org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver] (http--192.168.1.20-8080-6) Resolving exception from handler [deliveries.manager.controller.AdminSignUpController@455901d2]: org.springframework.web.bind.annotation.support.HandlerMethodInvocationException: Failed to invoke handler method [public java.lang.String deliveries.manager.controller.AdminSignUpController.submitProduct(javax.servlet.http.HttpServletRequest,org.springframework.ui.Model,deliveries.manager.pagebean.AdminSignupBean,org.springframework.validation.BindingResult,org.springframework.ui.ModelMap)]; nested exception is java.lang.IllegalStateException: JSR-303 validated property 'password.confirmPassword' does not have a corresponding accessor for Spring data binding - check your DataBinder's configuration (bean property versus direct field access)
00:16:11,897 DEBUG [org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver] (http--192.168.1.20-8080-6) Resolving exception from handler [deliveries.manager.controller.AdminSignUpController@455901d2]: org.springframework.web.bind.annotation.support.HandlerMethodInvocationException: Failed to invoke handler method [public java.lang.String deliveries.manager.controller.AdminSignUpController.submitProduct(javax.servlet.http.HttpServletRequest,org.springframework.ui.Model,deliveries.manager.pagebean.AdminSignupBean,org.springframework.validation.BindingResult,org.springframework.ui.ModelMap)]; nested exception is java.lang.IllegalStateException: JSR-303 validated property 'password.confirmPassword' does not have a corresponding accessor for Spring data binding - check your DataBinder's configuration (bean property versus direct field access)
00:16:11,900 DEBUG [org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver] (http--192.168.1.20-8080-6) Resolving exception from handler [deliveries.manager.controller.AdminSignUpController@455901d2]: org.springframework.web.bind.annotation.support.HandlerMethodInvocationException: Failed to invoke handler method [public java.lang.String deliveries.manager.controller.AdminSignUpController.submitProduct(javax.servlet.http.HttpServletRequest,org.springframework.ui.Model,deliveries.manager.pagebean.AdminSignupBean,org.springframework.validation.BindingResult,org.springframework.ui.ModelMap)]; nested exception is java.lang.IllegalStateException: JSR-303 validated property 'password.confirmPassword' does not have a corresponding accessor for Spring data binding - check your DataBinder's configuration (bean property versus direct field access)
00:16:11,902 DEBUG [org.springframework.web.servlet.DispatcherServlet] (http--192.168.1.20-8080-6) Could not complete request: org.springframework.web.bind.annotation.support.HandlerMethodInvocationException: Failed to invoke handler method [public java.lang.String deliveries.manager.controller.AdminSignUpController.submitProduct(javax.servlet.http.HttpServletRequest,org.springframework.ui.Model,deliveries.manager.pagebean.AdminSignupBean,org.springframework.validation.BindingResult,org.springframework.ui.ModelMap)]; nested exception is java.lang.IllegalStateException: JSR-303 validated property 'password.confirmPassword' does not have a corresponding accessor for Spring data binding - check your DataBinder's configuration (bean property versus direct field access)
        at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:181) [spring-web-3.0.4.RELEASE.jar:3.0.4.RELEASE]
        at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:427) [spring-webmvc-3.0.4.RELEASE.jar:3.0.4.RELEASE]

这是 bean 类:

@CompareStrings(propertyNames={"password", "confirmPassword"}, message="Password and Comfirm Password must be matched.")
public class AdminSignupBean {
   private String password = "";
        private String confirmPassword = "";
   private String emailAddress = "";
        private String firstName = "";
        private String lastName = "";
        private String gender = "M";
        private String loginName = "";
        private String address1 = "";
        private String address2 = "";
        private String suburb = "";
        private String state = "";
        private String postcode = "";
        private String phone = "";
...
<settings and getters>

验证器类:

public class CompareStringsValidator implements ConstraintValidator<CompareStrings, Object> {

    public enum StringComparisonMode {
        EQUAL, EQUAL_IGNORE_CASE, NOT_EQUAL, NOT_EQUAL_IGNORE_CASE
    }

    private String[] propertyNames;
    private StringComparisonMode comparisonMode;
    private boolean allowNull;

    @Override
    public void initialize(CompareStrings constraintAnnotation) {
        this.propertyNames = constraintAnnotation.propertyNames();
        this.comparisonMode = constraintAnnotation.matchMode();
        this.allowNull = constraintAnnotation.allowNull();
    }

    @Override
    public boolean isValid(Object target, ConstraintValidatorContext context) {
        boolean isValid = true;
        List<String> propertyValues = new ArrayList<String> (propertyNames.length);
        for(int i=0; i<propertyNames.length; i++) {
            String propertyValue = ConstraintValidatorHelper.getPropertyValue(String.class, propertyNames[i], target);
            if(propertyValue == null) {
                if(!allowNull) {
                    isValid = false;
                    break;
                }
            } else {
                propertyValues.add(propertyValue);
            }
        }

        if(isValid) {
            isValid = ConstraintValidatorHelper.isValid(propertyValues, comparisonMode);
        }

        if (!isValid) {
          /*
           * if custom message was provided, don't touch it, otherwise build the
           * default message
           */
          String message = context.getDefaultConstraintMessageTemplate();
          message = (message.isEmpty()) ?  ConstraintValidatorHelper.resolveMessage(propertyNames, comparisonMode) : message;

          context.disableDefaultConstraintViolation();
          ConstraintViolationBuilder violationBuilder = context.buildConstraintViolationWithTemplate(message);
          for (String propertyName : propertyNames) {
            NodeBuilderDefinedContext nbdc = violationBuilder.addNode(propertyName);
            nbdc.addConstraintViolation();
          }
        }    

        return isValid;
    }
}

控制器类:

public String submitProduct(HttpServletRequest req, Model model, 
          @ModelAttribute("adminsignup") @Valid final AdminSignupBean adminsignup,
                         BindingResult result, ModelMap map)
   {   
      UUID pbid=adminsignup.getPbid();
                logger.debug("====adminsignup:"+adminsignup.toString());

                if (result.hasErrors()) {
                    AdminSignupBean admin = products.get(pbid);
                    req.getSession().setAttribute("pbid", pbid);
                    BeanUtils.copyProperties(adminsignup, admin, new String[] {"pbid", "mpf", "password", "confirmPassword"});
                    map.addAttribute("adminsignupbean",admin);

                    return "adminsignup/createadmin";
                }
                Set<ConstraintViolation<AdminSignupBean>> violations 
                        = validatorFactory.getValidator().validate(adminsignup);
                for(ConstraintViolation<AdminSignupBean> violation : violations) {
                    logger.debug("=========Violation Message:- " + violation.getMessage());
                }
                if (!violations.isEmpty()) {
                    logger.debug("=====violations messages:"+violations.toString());
                    AdminSignupBean admin = products.get(pbid);
                    req.getSession().setAttribute("pbid", pbid);
                    BeanUtils.copyProperties(adminsignup, admin, new String[] {"pbid", "mpf", "password", "confirmPassword"});
                    map.addAttribute("adminsignupbean",admin);

                    return "adminsignup/createadmin";
                }

非常感谢任何建议。 谢谢 山姆

【问题讨论】:

    标签: hibernate validation bean-validation


    【解决方案1】:

    在我看来这是SpringValidatorAdapter 的问题。似乎此适配器帖子处理了约束违规并期望违规与现有实体属性匹配。这不是 Bean 验证方面的要求。我可以看到,在某些情况下,在您的自定义约束验证器中,您正在构建自定义错误。发生异常时是这样吗?

    我不是 Spring 专家,但我认为在 Spring 中集成 Bean Validation 有多种方法,特别是如果您只需要 JSR 303。例如,您是否看过 LocalValidatorFactoryBean

    【讨论】:

      【解决方案2】:

      我也遇到了这个问题。这确实是因为 Spring 的实现,更确切地说是 Spring 如何访问属性:通过 getter。如果某个属性出现验证错误,Spring bean 的逻辑将通过它的 getter 访问该属性。在这种情况下,getter getConfirmPassword() 丢失(或拼写错误)。在 Spring 中,每个属性都需要一个相应的 getter 进行验证。

      我也认为这是 Spring 的糟糕实现。一个因此而无法使用的用例:

      //The validator will make sure that each String in the list is a valid enum value
      @EnumValidator(enumClass = SomeEnum.class) 
      private final List<String> someEnumList;
      
      //Since the validation guarantees that each String is a valid enum, the conversion can happen here
      public List<SomeEnum> getSomeEnumList() {
          return someEnumList.stream().map(SomeEnum::valueOf).collect(Collectors.toList());
      }
      

      如果由于 Spring 的实现而传递了不正确的值,上面的代码将抛出 500 而不是 422:验证之后,正确地看到问题,Spring 将访问 getter 并抛出 InvocationException

      【讨论】:

        猜你喜欢
        • 2013-05-27
        • 2011-09-29
        • 2018-08-15
        • 2012-06-18
        • 2015-07-31
        • 2013-11-23
        • 2013-12-27
        • 2014-03-24
        • 2011-07-24
        相关资源
        最近更新 更多