15

I understand from the docs http://docs.spring.io/spring-data/rest/docs/2.1.2.RELEASE/reference/html/validation-chapter.html that I can declare validators with certain prefixes.

I'm using JSR 303 so my domain entities are annotated with validation annotations.

Can - and if yes, how - I use JSR 303 Bean Validation with Spring Data Rest?

PS: I'm using Spring Boot

Anirudh
  • 2,286
  • 4
  • 38
  • 64
Marcel Overdijk
  • 11,041
  • 17
  • 71
  • 110
  • Possible duplicate of [Spring-Data-Rest Validator](http://stackoverflow.com/questions/24318405/spring-data-rest-validator) –  Nov 09 '15 at 08:13

4 Answers4

15

This seems to work:

@Configuration
protected static class CustomRepositoryRestMvcConfiguration extends RepositoryRestMvcConfiguration {

    @Autowired
    private Validator validator;

    @Override
    protected void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
        validatingListener.addValidator("beforeCreate", validator);
        validatingListener.addValidator("beforeSave", validator);
    }
}
Marcel Overdijk
  • 11,041
  • 17
  • 71
  • 110
  • 2
    Created improvement request to do this automatically / configurable: https://jira.spring.io/browse/DATAREST-370 – Marcel Overdijk Aug 11 '14 at 07:00
  • 1
    Other way is to document this properly in Spring Data REST documentation: https://jira.spring.io/browse/DATAREST-372 – Marcel Overdijk Aug 12 '14 at 06:44
  • Problem with creating Validator instance within method configureValidatingRepositoryEventListener() is - validator cannot access other spring managed beans. It is good to define validator bean in application context and autowire in CustomRepositoryRestMvcConfiguration like shown above. – charybr Dec 23 '14 at 08:04
  • Perhaps I'm a little late to the party, but what about using the event [listeners](http://docs.spring.io/spring-data/rest/docs/2.3.0.M1/reference/html/#events-chapter) and putting @Valid on the parameters – Robin-Hoodie Feb 25 '15 at 07:26
  • 1
    If you are using Spring Boot, please see this issue https://github.com/spring-projects/spring-boot/issues/2392. To keep Spring Boot autoconfiguration you should be extending `SpringBootRepositoryRestMvcConfiguration` – Dmitry Chornyi Apr 24 '15 at 00:49
7

To customize the spring data-rest configuration, register a RepositoryRestConfigurer (or extend RepositoryRestConfigurerAdapter) and implement or override the configureValidatingRepositoryEventListener method for your specific use case.

public class CustomRepositoryRestConfigurer extends RepositoryRestConfigurerAdapter {

    @Autowired
    private Validator validator;

    @Override
    public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
        validatingListener.addValidator("beforeCreate", validator);
        validatingListener.addValidator("beforeSave", validator);
    }
}
herau
  • 1,466
  • 2
  • 18
  • 36
1

//Edit - Giving more information based on the comment for this answer and changing the code accordingly.

Related Documentation - http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc

Notes

//This is making the handler global for the application
//If this were on a @Controller bean it would be local to the controller
@ControllerAdvice

//Specifies to return a 400
@ResponseStatus(value = HttpStatus.BAD_REQUEST)

//Which exception to handle
@ExceptionHandler(ConstraintViolationException.class)

//Specifies to make the return value JSON.
@ResponseBody

//This class if for modeling the error we return.
//(Could use HashMap<String, Object> also if you feel it's cleaner)
class ConstraintViolationModel {

This is an exception handler for Spring that should work in spring boot just fine.

import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

@ControllerAdvice
public class ExceptionHandlingController {
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    public @ResponseBody List<ConstraintViolationModel> handleConstraintViolation(
            HttpServletRequest req, final ConstraintViolationException exception) {
        ArrayList<ConstraintViolationModel> list = new ArrayList<ConstraintViolationModel>();
        for (ConstraintViolation<?> violation : exception
                .getConstraintViolations()) {
            list.add(new ConstraintViolationModel(violation.getPropertyPath()
                    .toString(), violation.getMessage(), violation
                    .getInvalidValue()));
        }
        return list;
    }

    private static class ConstraintViolationModel {
        public String field;
        public String message;
        public Object invalidValue;

        public ConstraintViolationModel(String field, String message,
                Object invalidValue) {
            this.field = field;
            this.message = message;
            this.invalidValue = invalidValue;
        }
    }
}
Zergleb
  • 2,212
  • 15
  • 24
  • Yes your statement is correct but this validation exception is coming directly from JPA and hence the 500. When using coded validator Spring Data Rest returns 400 with errors in response. I want this behavior also with the annotations. Maybe my q was not clear enough. – Marcel Overdijk Aug 10 '14 at 06:06
  • 1
    Thanks again Zergleb. Spring Data REST already contains functionality to this. See also my own answer. – Marcel Overdijk Aug 11 '14 at 06:59
0

this (validatingListener.addValidator("beforeCreate", validator);) doesn't really fully work because the validation only manages the entities.

So if you try for example to put a validation on a non-entity you get a nasty error saying org.springframework.beans.NotReadablePropertyException: Invalid property '...' of bean class [... the non-entity one ...]: Bean property '....' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?

While obviously more laborious you can perform the validation directly on the Validator manually, e.g:

@Component("beforeSaveListingValidator")
public class BeforeSaveListingValidator implements Validator {

    @Autowired
    private LocalValidatorFactoryBean validator;

    @Override
    public void validate(Object object, Errors errors) {
        BindingResult bindingResult = new BeanPropertyBindingResult(object, errors.getObjectName());
        validator.validate(object, bindingResult);
        errors.addAllErrors(bindingResult);
Ignacio
  • 331
  • 6
  • 15