【问题标题】:Spring Web MVC validation by Hibernate Validator doesn't draw Errors in BindingResultHibernate Validator 的 Spring Web MVC 验证不会在 BindingResult 中绘制错误
【发布时间】:2017-07-28 06:06:12
【问题描述】:

我一直在我的 Spring 项目中使用 Hibernate Validator。我即将自动验证我的JUser 对象。即,我希望 Spring 验证对象并在 BindigResult 中设置错误。但它不起作用。

pom.xml

<properties>
    <spring.version>4.3.5.RELEASE</spring.version>
    <spring.security.version>4.0.2.RELEASE</spring.security.version>
    <hibernate.version>4.3.11.Final</hibernate.version>
    <validation-api.version>1.1.0.Final</validation-api.version>
    <hibernate-validator.version>5.4.0.Final</hibernate-validator.version>
</properties>
....

applicationContext.xml

...
<tx:annotation-driven transaction-manager="hibernateTransactionManager"/>
<context:annotation-config />
<context:component-scan base-package="my.project.controller" />
<mvc:annotation-driven validator="validator">
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basename" value="classpath:messages"/>
</bean>

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
</bean>
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
    <property name="validator" ref="validator"/>
</bean>
<bean id="localeResolver"
      class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
    <property name="defaultLocale" value="en" />
</bean>

JUser.java

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotEmpty;

@Entity
public class JUser implements Officeable {

   @Id
   private Long id;

   @Column(unique = true, nullable = false)
   private String username;

   private String password;

   @NotEmpty
   private String firstName;

   @NotEmpty
   private String lastName;
   private String tel;
}

UserController.java

import javax.validation.ConstraintViolationException;
....

@RequestMapping(value = "/update", method = RequestMethod.POST)
public String update2(HttpServletRequest request, Model model, @ModelAttribute("user") @Valid JUser user, BindingResult result) {
    if (!result.hasErrors()) {
        System.out.println("binding result has no errors for user ");
        try {
            JUser updated = userService.update(user);
            model.addAttribute("user", updated);
        } catch (MessageException | DataIntegrityViolationException ex) {
            result.reject("user", ex.getMessage());
        } catch (ConstraintViolationException cvex) {
            for (ConstraintViolation cv : cvex.getConstraintViolations()) {
                result.rejectValue(cv.getPropertyPath().toString(),cv.getMessageTemplate() , cv.getMessage());
            }
        }
    }
    return "user/manage";
}

正如您在上面的控制器方法中看到的,我希望 Spring 验证 user 对象并在 BindigResult 中设置错误。但它不起作用。
例如当user 有空firstName 我面对输出:

output:
binding result has no errors for user 

我必须手动捕获休眠引发的异常:

 ConstraintViolationException: may not be empty ...

更多描述。我使用了 String @Validated 注释,但效果不佳。我已经阅读了十多个相关的 stackoverflow 问题,但它们并没有解决我的问题。

【问题讨论】:

  • 不确定您要进行什么样的验证,您还可以添加 JUser 对象的内容吗?
  • 通过验证 API 注释进行简单验证。问题已编辑。

标签: spring validation spring-mvc hibernate-validator spring-validator


【解决方案1】:

如果你使用 HibernateValidator,你必须告诉你使用 HibernateValidator 类

通过查看 LocalValidatorFactoryBean javadoc

当通过 Spring 或 JSR-303 Validator 接口与此 bean 的实例对话时,您将与底层 ValidatorFactory 的默认 Validator 对话。这非常方便,因为您不必再​​对工厂执行另一个调用,假设您几乎总是会使用默认的验证器。这也可以直接注入到任何类型 Validator 的目标依赖中!

所以您应该使用setProviderClass 方法来指定要使用的类

这就是我所做的(我使用的是基于注释的配置,但它是相同的):

WebMvcConfig

@Override
public Validator getValidator() {

    LocalValidatorFactoryBean lvfb = new LocalValidatorFactoryBean();
    lvfb.setProviderClass(HibernateValidator.class);
    return lvfb;
}

型号

@Entity
@Table(name = "CANDIDATO")
public class Candidato extends AbstractModel {

    private static final long serialVersionUID = -5648780121365553697L;
    .
    .
    .
    private String corsoLaurea;
    .
    .
    .
    @Column(name="CORSO_LAUREA", nullable=true)
    @NotEmpty
    public String getCorsoLaurea() {
        return corsoLaurea;
    }
}

控制器方法

@RequestMapping(method = { RequestMethod.PUT }, value = { "/salvaModificheCandidato" })
public ResponseEntity<BaseResponse<String>> modificaCandidato(@RequestBody @Valid ModificaCandidatoDto dto, BindingResult bindResult) throws Exception
{
    BaseResponse<String> result = null;
    HttpStatus status = null;
    try
    {
        this.candidatoSvc.modificaCandidato(dto);
        result = new BaseResponse<String>();
        status = HttpStatus.OK;
        result.setDescrizioneOperazione("Aggiornamento candidato terminato correttamente");
        result.setEsitoOperazione(status.value());
        result.setPayload(Collections.EMPTY_LIST);
    }
    catch (Exception e)
    {
        result = new BaseResponse<String>();
        status = HttpStatus.INTERNAL_SERVER_ERROR;
        String message = "Errore nella modifica del candicato con ID "+dto.getIdCandidato()+"; "+e.getMessage();
        logger.error(message, e);
        result.setDescrizioneOperazione(message);
        result.setEsitoOperazione(status.value());
    }
    return new ResponseEntity<BaseResponse<String>>(result, status);
}

使用此配置,我发现 DTO 和模型的 bindinresult 错误

希望对你有用

编辑部分

我看到您的问题是当您尝试持久化对象时绑定结果不为空;我用这种方式更改了我的代码

模型没有变化(我使用了hibernate验证NotEmpty注解)

我是这样改变我的服务方式的:

@Override
@Transactional(transactionManager = "hibTx", rollbackFor = CandidatiDbException.class, readOnly = false)
public void modificaCandidato(ModificaCandidatoDto dto, BindingResult brErrors) throws CandidatiDbException {
    try 
    {
        dao.modificaCandidato(dto, brErrors);
    } catch (Exception e) 
    {

        String message = "Errore nella modifica del candidato con ID "+dto.getIdCandidato()+"; "+e.getMessage();
        logger.error(message, e);
        throw new CandidatiDbException(message);
    }

}

如您所见,我将BindingResult 对象传递给方法

然后我以这种方式更改了我的 DAO impl:

public class CandidatoDaoImpl<T> implements ICandidatoDao<T> {

    @Autowired
    @Qualifier("candValidator")
    Validator validator;
    public void modificaCandidato(ModificaCandidatoDto dto, BindingResult brErrors) {
        Session sessione = getSession();
        sessione.setCacheMode(CacheMode.IGNORE);
        Candidato candidato = sessione.load(Candidato.class, dto.getIdCandidato());
        .
        .
        .
        validator.validate(candidato, brErrors);
        if( !brErrors.hasErrors() )
        {

            sessione.saveOrUpdate(candidato);
        }
    }

}

最后我以这种方式更新了我的 WebMvcConfig:

@Configuration
@EnableWebMvc
@Import(SharedSpringConfig.class)
@PropertySource( value={"classpath:configuration.properties"}, encoding="UTF-8", ignoreResourceNotFound=false)
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    @Bean(name="candValidator")
    public Validator validator()
    {
        LocalValidatorFactoryBean lvfb = new LocalValidatorFactoryBean();
        lvfb.setProviderClass(HibernateValidator.class);
        return lvfb;
    }
    @Override
    public Validator getValidator() {
        return validator();
    }
}

通过这种方式,当我想要保留的对象出现错误时,我的 BindingResult 对象不为空,并且不会引发异常

希望对你有用

安杰洛

【讨论】:

  • 我也是这么做的。但它不起作用。估计是版本问题。因为它在更新到 Hibernate Validator 5 之前可以正常工作。你使用什么版本的 Hibernate Validator?
  • 这很奇怪;我正在使用它,它正在工作....您可以发布您更新的代码/配置吗?
  • 你使用什么 Spring 版本?
  • 我正在使用 spring 4.3.6.RELEASE。现在我不能,但我会尽快将我的代码放在 github 上......希望它有用
  • 我使用xml配置。如果你看到问题文本和你的注解配置一样:&lt;property name="providerClass" value="org.hibernate.validator.HibernateValidator"/&gt;
【解决方案2】:

您可以使用 ExceptionHandler 方法。只需在您的控制器类中添加此方法即可。我还没有用@ModelAttribute 测试过这个,虽然它应该可以工作,但我确信它可以和@RequestBody 一起工作。

@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ErrorDTO processValidationError(MethodArgumentNotValidException ex) {
    BindingResult result = ex.getBindingResult();
    List<FieldError> fieldErrors = result.getFieldErrors();

    // your own custom error dto class 
    ErrorDTO errorDto = constructErrors(fieldErrors);

    return errorDto;
}

【讨论】:

  • BindingResult 结果?呵呵!仔细阅读问题。我的问题是 BindingResult 是空的!
  • 你用 ResponseBody 注解试过了吗?
  • 但我不想返回@ResponseBody。我想返回一个 jsp 页面并在表单中显示 BindingResult 错误。 gist.github.com/dariushvb2010/f3659fa459646e480ac06edc9d6f8bd3
  • 你的意思是我在控制器方法中构造了一个包含 BindingResult 的自定义异常。但是 BindingResult 为空时没有意义。
  • 调用异常处理程序时 ex.getBindingResult 是否为空?
【解决方案3】:

根据spring-mvc-4.3.xsd

要用于验证的 Validator 的 bean 名称 控制器模型对象。该属性不是必需的,只有 如果需要配置自定义验证器,则需要指定。如果 未指定,如果 JSR-303 将安装 JSR-303 验证 provider 存在于类路径中。

我没有看到您编写了任何自定义验证器,因此您可以更改

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

支持默认的 JSR-303

<mvc:annotation-driven />

示例:Spring 3 MVC and JSR303 @Valid example


更新 1

您也可以尝试删除validation-api.version

这会传递对 Bean Validation API 的依赖关系 (javax.validation:validation-api:1.1.0.Final)。

【讨论】:

  • 我已经测试过了。它也不起作用。它没有效果。
  • 您能否也将NezBazObjectMapperStringToUser 添加到gist 中
  • 是简单的jackson ObjectMapper。 StringToUser 只是从 db 读取用户并返回它。我会尽快更新要点。
【解决方案4】:

首先,你能在添加下面的代码后测试验证是否有效吗?

pom.xml
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>5.2.4.Final</version>
    </dependency>

@Bean // in configuration
    public Validator validator() {
        ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
        return validatorFactory.getValidator();
    }


@Autowired //in controller
private Validator validator;

public <T> void validate(T t) {
    Set validate = this.validator.validate(t);
    if(!validate.isEmpty()) {
        throw new RuntimeException();
    }
}

如果这可行,那么可以建议您进一步简化它。

【讨论】:

  • 我希望 Spring 验证对象并在 BindigResult 中设置错误
  • Hibernate Validator 没有任何问题。我认为它没有与 Spring 集成。我希望 Spring 验证对象并在 BindigResult 中设置错误。我想通过控制器中的@Valid 注释自动验证。我测试了你的代码并且它正在工作。但这不是解决方案。
  • 可以尝试在配置中添加Validator bean,然后尝试@valid
  • 兄弟!你在开玩笑吗?我已经完全做到了。它不工作。
猜你喜欢
  • 2013-08-12
  • 2011-03-05
  • 1970-01-01
  • 2019-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-19
相关资源
最近更新 更多