Spring Data Validation

Seeking help with Spring data validation to properly handle validation errors:

I am so confused by the docs regarding spring-data-rest validation here: http://docs.spring.io/spring-data/rest/docs/current/reference/html/#validation

I am trying to correctly handle validation for a POST call that is trying to save a new company object

I got this object:

@Entity public class Company implements Serializable { @Id @GeneratedValue private Long id; @NotNull private String name; private String address; private String city; private String country; private String email; private String phoneNumber; @OneToMany(cascade = CascadeType.ALL, mappedBy = "company") private Set<Owner> owners = new HashSet<>(); public Company() { super(); } 

...

and this parameter is RestResource dao

 import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.rest.core.annotation.RestResource; import com.domain.Company; @RestResource public interface CompanyDao extends PagingAndSortingRepository<Company, Long> { } 

POST Request for api / Companies:

 { "address" : "One Microsoft Way", "city" : "Redmond", "country" : "USA", "email" : "info@microsoft.com", "phoneNumber" : "(425) 703-6214" } 

When I issue a POST with a null name, I get the following rest response with httpcode 500

{"timestamp": 1455131008472, "status": 500, "error": "Internal Server Error", "exception": "javax.validation.ConstraintViolationException", "message": "Validation error for classes [com. domain.Company ] during save for groups [javax.validation.groups.Default,] \ nList restriction violations: [\ n \ tConstraintViolationImpl {interpolationMessage = 'cannot be null ", propertyPath = name, rootBeanclass = class com.domain.Company, messageTemplate = '{javax.validation.constraints.NotNull.message}'} \ n] "," path ":" / api / companies / "}

I tried to create the following bean, but it never does anything:

 @Component(value="beforeCreateCompanyValidator") public class BeforeCreateCompanyValidator implements Validator{ @Override public boolean supports(Class<?> clazz) { return Company.class.isAssignableFrom(clazz); } @Override public void validate(Object arg0, Errors arg1) { System.out.println("xxxxxxxx"); } } 

and even if it worked, how would it help me develop a better answer to the error with the proper http code and clear json answer?

so confusing

using 1.3.2.RELEASE

 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> 
+11
spring-boot spring-data-rest spring-rest
source share
3 answers

@Mathias, it seems the following is enough to check jsr 303 annotations and automatically return http code 400 with nice messages (I don't even need the BeforeCreateCompanyValidator or BeforeSaveCompanyValidator classes):

 @Configuration public class RestValidationConfiguration extends RepositoryRestConfigurerAdapter{ @Bean @Primary /** * Create a validator to use in bean validation - primary to be able to autowire without qualifier */ Validator validator() { return new LocalValidatorFactoryBean(); } @Override public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) { Validator validator = validator(); //bean validation always before save and create validatingListener.addValidator("beforeCreate", validator); validatingListener.addValidator("beforeSave", validator); } 

}

400 answer:

 { "errors": [{ "entity": "Company", "message": "may not be null", "invalidValue": "null", "property": "name" }, { "entity": "Company", "message": "may not be null", "invalidValue": "null", "property": "address" }] } 
+8
source share

I think your problem is that the bean check is too late - this is done at the JPA level before continuing. I found that - unlike spring mvc - spring -data-rest does not perform a bean check when the controller method is called. To do this, you will need additional configuration.

You want spring-data-rest to check your bean - this will give you good error answers and the correct HTTP return code.

I set up my check in spring -data-rest as follows:

 @Configuration public class MySpringDataRestValidationConfiguration extends RepositoryRestConfigurerAdapter { @Bean @Primary /** * Create a validator to use in bean validation - primary to be able to autowire without qualifier */ Validator validator() { return new LocalValidatorFactoryBean(); } @Bean //the bean name starting with beforeCreate will result into registering the validator before insert public BeforeCreateCompanyValidator beforeCreateCompanyValidator() { return new BeforeCreateCompanyValidator(); } @Override public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) { Validator validator = validator(); //bean validation always before save and create validatingListener.addValidator("beforeCreate", validator); validatingListener.addValidator("beforeSave", validator); } } 

When the bean check and / or my custom search error checks I get 400 - a bad request with a payload like this:

  Status = 400 Error message = null Headers = {Content-Type=[application/hal+json]} Content type = application/hal+json Body = { "errors" : [ { "entity" : "siteWithAdminUser", "message" : "may not be null", "invalidValue" : "null", "property" : "adminUser" } ] } 
+4
source share

The answers of @Mathias and @ 1977 are enough for regular Spring Data REST calls. However, in cases where you need to write custom @RepositoryRestController using @RequestBody and @Valid , JSR-303 annotations did not work for me.

So, as a complement to the answer, in the case of a custom @RepositoryRestController with the annotation @RequestBody and @Valid I added the following @ControllerAdvice :

 /** * Workaround class for making JSR-303 annotation validation work for controller method parameters. * Check the issue <a href="https://jira.spring.io/browse/DATAREST-593">DATAREST-593</a> */ @ControllerAdvice public class RequestBodyValidationProcessor extends RequestBodyAdviceAdapter { private final Validator validator; public RequestBodyValidationProcessor(@Autowired @Qualifier("mvcValidator") final Validator validator) { this.validator = validator; } @Override public boolean supports(final MethodParameter methodParameter, final Type targetType, final Class<? extends HttpMessageConverter<?>> converterType) { final Annotation[] parameterAnnotations = methodParameter.getParameterAnnotations(); for (final Annotation annotation : parameterAnnotations) { if (annotation.annotationType().equals(Valid.class)) { return true; } } return false; } @Override public Object afterBodyRead(final Object body, final HttpInputMessage inputMessage, final MethodParameter parameter, final Type targetType, final Class<? extends HttpMessageConverter<?>> converterType) { final Object obj = super.afterBodyRead(body, inputMessage, parameter, targetType, converterType); final BindingResult bindingResult = new BeanPropertyBindingResult(obj, obj.getClass().getCanonicalName()); validator.validate(obj, bindingResult); if (bindingResult.hasErrors()) { throw new RuntimeBindException(bindingResult); } return obj; } } 
+2
source share

All Articles