12

I'm trying to get the validation in spring-data-rest to work. From the documentation you only need to make a validator available, and I've got that to work, but when a validation constraint is successfully caught/processed I get a 500 error page with the stack trace.

In the config class, RepositoryRestMvcConfiguration it has a validationExceptionHandler which looks like it should get such validation errors to return as 400 rather than 500. It is also a lazy loaded bean.

Do I have an incorrect setup? or is there another way to get spring-data-rest to return 400 instead of 500?

I'm using spring-data-rest version 2.0.0 Release

Stack trace return by tomcat:

HTTP Status 500 - Request processing failed; nested exception is javax.validation.ConstraintViolationException: Validation failed for classes [test.domain.Account] during persist time for groups [javax.validation.groups.Default, ]

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is javax.validation.ConstraintViolationException: Validation failed for classes [test.domain.Account] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
    ConstraintViolationImpl{interpolatedMessage='size must be between 0 and 10', propertyPath=login, rootBeanClass=class test.domain.Account, messageTemplate='{javax.validation.constraints.Size.message}'}
]
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:965)
    org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:855)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:829)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

Account Entity:

@Entity
public class Account {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;

    @Column(unique = true)
    @Size(max = 10)
    String login;

}

RestMvcConfig:

@Configuration
public class RestExporterRestConfig extends RepositoryRestMvcConfiguration {}
wenic
  • 1,169
  • 2
  • 14
  • 23
  • 1
    it should return 400, as it bad request from the client. You can set the status by using response.setStatus(400); – Touchstone Feb 26 '14 at 06:41
  • yes I agree it should return 400, the issue I'm raising is that I don't think spring-data-rest is correctly handling these validation errors by returning 400, so I think I have missing something in the configurations or the validationExceptionHandler bean is not being load or used. – wenic Feb 26 '14 at 09:39
  • You are getting 500 due to the exception at server side(which is quite appropriate). Moreover, you should handle this exception and manually set the status code to 400. – Touchstone Feb 26 '14 at 10:32
  • 1
    which is why I think the validationExceptionHandler in the RepositoryRestMvcConfiguration is suppose to be doing, the cause is of type javax.validation.ConstraintViolationException which it suppose to take and turn it into a 400.So either the bean is not being loaded or it never used. I'll will try set tomcat into debug mode and see if that bean actually get initialized. – wenic Feb 26 '14 at 13:52

3 Answers3

4

Seem to have got it working; i had to override the validatingRepositoryEventListener() and manually add validators to the listener;

@Configuration
public class RestExporterRestConfig extends RepositoryRestMvcConfiguration {

    @Bean
    public Validator validator() {
        return new LocalValidatorFactoryBean();
    }

    @Bean
    @Override
    public ValidatingRepositoryEventListener validatingRepositoryEventListener() {
        ValidatingRepositoryEventListener listener = new ValidatingRepositoryEventListener();
        configureValidatingRepositoryEventListener(listener);
        listener.addValidator("afterCreate", validator());
        listener.addValidator("beforeCreate", validator());
        return listener;
    }

}

I now get a 400 returned as follows;

400 Bad Request
{"errors":
    [{  "entity":"Account",
        "message":"size must be between 0 and 10",
        "invalidValue":"login 0dsfdsfdsfdsfdsfdsfdsfds",
        "property":"login"
    }]
}
wenic
  • 1,169
  • 2
  • 14
  • 23
  • Did you ever have further trouble with this? I have this setup, but only get the above response for some fields. For other fields, I'm still getting the ConstraintViolationException. – bvulaj Jun 03 '16 at 02:53
4

The previous answers didn't work for me, I think due to changes in Spring Data Rest so here is an updated answer that did work with JPA and MongoDb to save anyone else spending ages on this.

Had to add this to my build.gradle dependencies

    compile('org.hibernate:hibernate-validator:4.2.0.Final')

and this config class

@Configuration
public class CustomRepositoryRestConfigurerAdapter extends RepositoryRestConfigurerAdapter {


   @Bean
   public Validator validator() {
       return new LocalValidatorFactoryBean();
   }

   @Override
   public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
       validatingListener.addValidator("afterCreate", validator());
       validatingListener.addValidator("beforeCreate", validator());
       validatingListener.addValidator("afterSave", validator());
       validatingListener.addValidator("beforeSave", validator());
   }
}
Romell
  • 497
  • 3
  • 13
0

Now that Java implements default methods on interfaces, Spring has deprecated the Adapter types. You can implement the solution provided by Romell as shown below. Personally, I'm not as concerned with the after methods, but feel free to choose on your own.

@Configuration
public class CustomRepositoryRestConfigurer implements RepositoryRestConfigurer {
    @Bean
    public Validator validator() {
        return new LocalValidatorFactoryBean();
    }
    @Override
    public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
        validatingListener.addValidator("beforeCreate", validator());
        validatingListener.addValidator("beforeSave", validator());
    }
}
el8ted
  • 1
  • 2