Spring Web MVC validation using Hibernate Validator does not cause BindingResult error

I am using Hibernate Validator in my Spring project. I am going to automatically check my JUser object. those. I want Spring to check the object and set errors in BindigResult. But that does not work.

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"; } 

As you see in the controller method above, I want Spring to check the user object and set errors in BindigResult . But that does not work. For example, when user has an empty firstName , I am faced with the output:

 output: binding result has no errors for user 

and I have to manually catch the sleeping exceptions:

  ConstraintViolationException: may not be empty ... 

detailed description . I used the String @Validated annotation and it did not work. I read over ten stackoverflow related questions and they did not solve my problem.

+7
spring spring-mvc spring-validator validation hibernate-validator
source share
4 answers

First, can you verify if validation works after adding the code below?

 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(); } } 

If this works, then you can suggest that you simplify it.

+1
source share

If you are using a HibernateValidator, you must say to use the HibernateValidator class

By exploring javadoc LocalValidatorFactoryBean

When you talk to an instance of this bean via the Spring or JSR-303 Validator interfaces, you will access the default validator of the base ValidatorFactory. This is very convenient because you do not need to make another call to the factory, assuming that you will almost always use the default validator. It can also be entered directly into any target dependency type Validator!

So you should use the setProviderClass method to indicate which class to use

Here is what I did (I am using annotation based configuration, but it is the same):

Webmvcconfig

 @Override public Validator getValidator() { LocalValidatorFactoryBean lvfb = new LocalValidatorFactoryBean(); lvfb.setProviderClass(HibernateValidator.class); return lvfb; } 

Model

 @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; } } 

controller method

 @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); } 

In this configuration, I detect bindinresult errors for DTO and Model

I hope this can be useful

SEPARATE PART

I saw that your problem is that bindresult is not empty when you try to save your object; So I changed the code

No model changes (I used the NotEmpty annotation to verify hibernation)

I changed my maintenance method as follows:

 @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); } } 

As you can see, I passed the BindingResult object BindingResult method

Then I changed my DAO impl like this:

 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); } } } 

Finally, I updated my WebMvcConfig as follows:

 @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(); } } 

That way, when I have some error in the object that I want to save, I have a BindingResult object that is not empty and no exception is thrown

I hope this can be useful

Angelo

+1
source share

By spring-mvc-4.3.xsd

bean is the name of the validator that should be used to validate the controller model objects. This attribute is not required, and you only need to specify if you need to configure a custom validator. If not specified, a JSR-303 check will be installed if the JSR-303 provider is present in the classpath.

I don’t see you write any custom validator to change

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

to support standard JSR-303

 <mvc:annotation-driven /> 

Example: Spring 3 MVC and JSR303 @Valid example


Update 1

Could you also try to remove validation-api.version

This transitively draws a dependency on the bean API validation (Javax.validation: validation-API: 1.1.0.Final).

+1
source share

You can use the ExceptionHandler approach. Just add this method to your controller class. I have not tested this with @ModelAttribute , although it should work, I know for sure that it works with @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; } 
+1
source share

All Articles