我在Spring项目中一直使用Hibernate Validator。我即将自动验证我的JUser
对象。即,我希望Spring验证Object并在BindigResult中设置错误。但它不起作用。
<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>
....
...
<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>
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;
}
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
我必须手工捕获hibernate引发的异常:
ConstraintViolationException: may not be empty ...
更多说明。我使用了字符串@Validated
注释,它也没有用。我已经阅读了十多个相关的stackoverflow问题,并没有解决我的问题。
答案 0 :(得分:1)
首先,您可以在添加以下代码后测试验证是否正常工作吗?
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();
}
}
如果这样可行,那么可以建议您进一步简化它。
答案 1 :(得分:1)
如果您使用的是HibernateValidator,则必须告诉您使用HibernateValidator类
查看LocalValidatorFactoryBean javadoc
当通过Spring或JSR-303 Validator接口与该bean的实例进行通信时,您将与底层ValidatorFactory的默认Validator进行通信。这非常方便,因为您不必在工厂执行另一个调用,假设您几乎总是会使用默认的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对象不是空的,并且没有异常被引发
我希望这可能有用
安吉洛
答案 2 :(得分:1)
要用于验证的Validator的bean名称 控制器模型对象。此属性不是必需的,也是唯一的 如果需要配置自定义Validator,则需要指定。如果 未指定,如果是JSR-303,将安装JSR-303验证 提供程序存在于类路径中。
我没有看到您编写了任何自定义验证程序,因此您可以更改
<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:验证-API:1.1.0.Final)。
答案 3 :(得分:1)
您可以使用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;
}