I am doing a spring boot rest application with a custom exception mapping. The exception mapping is irrelevant, but I wanted to use hibernate validator in version 4 ((because I have to use java validation api version 1.0).
Now I am initializing the validators in #configuration class:
#Bean
public Validator validator() {
return new LocalValidatorFactoryBean();
}
#Bean
MethodValidationPostProcessor methodValidationPostProcessor(Validator validator) {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setValidator(validator);
return processor;
}
And I create a resource:
#Path("/validation")
#Produces("application/json")
#Validated
public interface ValidationTestResource {
#POST
#Path("/test")
#Validated
public void translatedAndMappedException(#RequestBody #Valid
ValidationTestDto testDto);
}
}
The resource works. And the validation works. But whenever I violate the constraints on ValidationTestDto I get an exception. However this is org.hibernate.validator.method.MethodConstraintViolationException and it only specified which method and which parameter failed to validate (translatedAndMappedException, argument testDto). This makes it kind of useless to return the violated field list, as such list is not present.
But If I use hibernate-validator version 5+ I get a proper javax.validation.ConstraintViolationException and I can extract a full list of fields that were not validated.
The question is:
1.If I am simply doing something wrong, and Hibernate validator 4 is like that.
2.If I can use something from spring to validate and forget about hibernate-validator that would do it properly?
Spring provides its own validation interfaces, so you can try using those directly. The documentation is here. You can use a BindingResult to see the details of the validation errors that are produced.
Related
I'm trying to validate my JPA Entity with #Valid like this:
public static void persist(#Valid Object o)
It worked fine for a while but now it stopped working and I'm not sure why. I tried to do it manually inside the persist method, and it works as expected:
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(o);
if (!constraintViolations.isEmpty()) {
throw new ConstraintViolationException(constraintViolations);
}
What could be happening or how can I debug this?
Method Validations is only available starting in bean validation v 1.1 (e.g. hibernate validator 5.x impl) which is part of Java EE 7 only. On top of that to have it working without extra specific BV code your method must be part of a component which is integrated with bean validation (eg. CDI Beans, JAX-RS Resource). Your custom code works because you do not use method validation but rather BV constraints which are defined directly on your object.
Won't work on arbitrary services. In Jersey it will only work for resource methods. So validate the incoming DTO in your resource method.
#POST
public Response post(#Valid SomeDTO dto) {}
See more at Bean Validation Support
UPDATE
So to answer the OP's comment about how we can make it work on arbitrary services, I created a small project that you can plug and play into your application.
You can find it on GitHub (jersey-hk2-validate).
Please look at the tests in the project. You will find a complete JPA example in there also.
Usage
Clone, build, and add it your Maven project
public interface ServiceContract {
void save(Model model);
}
public class ServiceContractImpl implements ServiceContract, Validatable {
#Override
public void save(#Valid Model model) {}
}
Then use the ValidationFeature to bind the service
ValidationFeature feature = new ValidationFeature.Builder()
.addSingletonClass(ServiceContractImpl.class, ServiceContract.class).build();
ResourceConfig config = new ResourceConfig();
config.register(feature);
The key point is to make your service implementation implement Validatable.
The details of the implementation are in the README. But the gist of it is that it makes use of HK2 AOP. So your services will need to be managed by HK2 for it to work. That is what the ValidationFeature does for you.
I have a Spring AOP aspect used for logging, where a method can be included for logging by adding an annotation to it, like this:
#AspectLogging("do something")
public void doSomething() {
...
}
I've been using this on Spring beans and it's been working just fine. Now, I wanted to use it on a REST-service, but I ran into some problems. So, I have:
#Path("/path")
#Service
public class MyRestService {
#Inject
private Something something;
#GET
#AspectLogging("get some stuff")
public Response getSomeStuff() {
...
}
}
and this setup works just fine. The Rest-service that I'm trying to add the logging to now has an interface, and somehow that messes stuff up. As soon as I add the #AspectLogging annotation to one of the methods, no dependencies are injected in the bean, and also, the aspect is newer called!
I've tried adding an interface to the REST-service that works, and it gets the same error.
How can having an interface lead to this type of problems? The aspect-logger works on classes with interfaces elsewhere, seems it's only a problem when it's a REST-service..
Ref the below Spring documentation (para 2) -
To enable AspectJ annotation support in the Spring IoC container, you
only have to define an empty XML element aop:aspectj-autoproxy in your
bean configuration file. Then, Spring will automatically create
proxies for any of your beans that are matched by your AspectJ
aspects.
For cases in which interfaces are not available or not used in an
application’s design, it’s possible to create proxies by relying on
CGLIB. To enable CGLIB, you need to set the attribute
proxy-targetclass= true in aop:aspectj-autoproxy.
In case your class implements an interface, a JDK dynamic proxy will be used. However if your class does not implement any interfaces then a CGLIB proxy will be created. You can achieve this #EnableAspectJAutoProxy. Here is the sample
#Configuration
#EnableAspectJAutoProxy
public class AppConfig {
#Bean
public LoggingAspect logingAspect(){
return new LoggingAspect();
}
}
#Component
#Aspect
public class LoggingAspect {
...
...
}
In my opinion what you are actually trying to do is to add spring annotations to a class maintained by jersey. In the result you are receiving a proxy of proxy of proxy of somethng. I do not think so this is a good idea and this will work without any problems. I had a similar issue when I tried to implement bean based validation. For some reasons when there were #PahtParam and #Valid annotations in the same place validation annotations were not visible. My advice is to move your logging to a #Service layer instead of #Controller.
I've followed the instructions in the Spring 4.0.5 documentation for configuring Bean Validation in the container, with Hibernate Validator 5.1 and this (Groovy) configuration class:
#Configuration("validationConfig")
#Import(CreatorConfig)
#ImportResource("/META-INF/spring/mockito-mocks.xml")
static class Config {
#Bean
validator() {
new LocalValidatorFactoryBean()
}
#Bean
mvpp() {
new MethodValidationPostProcessor()
}
}
My class being tested is instantiated in the referenced CreatorConfig class:
#Configuration
#DependsOn("validationConfig")
static class CreatorConfig {
#Bean
ticketCreator(TicketRepository tickets) {
new UploadTicketCreator(tickets)
}
}
I separated this out into a separate configuration class under the belief that I needed the postprocessor registered before creating the UploadTicketCreator bean, which is annotated according to the documentation:
#Validated
public class UploadTicketCreator {
public UploadTicket createTicket(#Valid CreateTicketRequest request) {
// do stuff
}
}
I'm then injecting the UploadTicketCreator into the test case. I've verified that the autowiring is operating properly (the field is populated, and its own tickets field is a Mockito mock). However, the validation logic is not being applied to my creator bean, and the injected bean is the raw POJO without any proxying.
Am I missing a piece of the validation setup? Is there an additional required step not mentioned in the documentation?
The Spring configuration processor appears to be inspecting the declared return type of the bean declarations. I was using Groovy's implicit return type, which presumably was compiled to Object, and adding an explicit return type of MethodValidationPostProcessor or even BeanPostProcessor caused the validation advice to be applied.
Lets say I define a POJO with parameters that is passed to a REST call
class MyVO {
#NotNull
#PathParam("name")
private String name;
#NotNull
#PathParam("age")
private Integer age;
// getters and setters
}
public class RESTclass {
public postData( #Form MyVO vo ) {
}
}
It automatically binds the objects in MyVO. But where do I get the validation errors?
Does it trigger the validation during binding? If not, how to trigger the validations?
Spring does all these well. It has BindingResult parameter that you can inject.
What is the equivalent here?
Any idea?
RestEasy Versions Prior to 3.0.1.Final
For bean validation 1.0, Resteasy has a custom validation provider that uses hibernate's bean validator implementation under the covers.
In order to get validation up and running in Resteasy you need to do the following:
Add the resteasy-hibernatevalidator-provider dependency to your project. Here is the maven pom entry if you are using maven:
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-hibernatevalidator-provider</artifactId>
<version>${resteasy.version}</version>
</dependency>
Annotate your resource classes where you want validation to occur with the #ValidateRequest annotation.
#Named
#Path("/users")
#ValidateRequest
public class UserResource extends BaseResource
{
#POST
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public Response createUser(#Valid User user)
{
//Do Something Here
}
}
Resteasy will automatically detect the HibernateValidatorAdapter on the classpath and begin invoking bean validation.
Create an ExceptionMapper<MethodConstraintViolationException> implementation to handle the validation errors.
Unlike in Spring where you have to check the BindingResult, when validation errors are encountered in Resteasy the hibernate validator will throw a MethodConstraintViolationException. The MethodConstraintViolationException will contain all of the validation errors within it.
#Provider
public class MethodConstraintViolationExceptionMapper extends MyBaseExceptionMapper
implements ExceptionMapper<MethodConstraintViolationException>
{
#Override
public Response toResponse(MethodConstraintViolationException exception)
{
//Do Something with the errors here and create a response.
}
}
RestEasy Version 3.0.1.Final
The latest version of Resteasy is now supporting bean validation spec 1.1 and has changed the api and exceptions thrown.
Instead of the resteasy-hibernatevalidator-provider you are going
to need the resteasy-validator-provider-11 dependency.
You will not need to add #ValidateRequest to your resource classes
as validation testing is turned on by default with
resteasy-validator-provider-11.
Instead of throwing a MethodConstraintViolationException when
violations are detected, an instance of RESTEasyViolationException
will be thrown.
Documentation: 3.0.1.Final Validation Documentation
I'm looking to see whether or not the following is even possible, as all preliminary searches haven't turned back anything to indicate either way.
I'd like to use Hibernate's Validator annotations to validate bean methods, and I would like to use some AOP framework (Spring, AOP Alliance, AspectJ, etc.) to intercept methods annotated with a subset of the Hibernate Validator annotations (#NotNull, #NotEmpty, #Email, etc.); I then want AOP advice to run when they are encountered.
Is this possible to do? If so, I am having a tough time visualizing how the code would work. Using Spring AOP's MethodInterceptor interface as an example:
First, the bean using Hibernate Validator:
public class SomeBean
{
private String data;
// Hibernate Validator annotation specifying that "data" cannot be an empty
// string.
#NotEmpty
public String getData() { ... } // etc.
}
Then, some code using that bean:
public void someMethod()
{
SomeBean oBean = new SomeBean();
// Validation should fail because we specified that "data" cannot be empty.
oBean.setData("");
}
Next, the AOP advice to be ran when Hibernate Validator-annotated methods are encountered.
public class ValidationInterceptor implements MethodInterceptor
{
public Object invoke(MethodInvocation invocation)
{
// Here's where we would use Hibernate's validator classes.
// My code example here is wrong, but it gets the point across.
Class targetClass = invocation.getClass(); // Should give me SomeBean.class
ClassValidator<targetClass> oValidator= new ClassValidator<targetClass>();
// Here I need to get a reference to the instance of the offending
// SomeBean object whose data has been set to empty...not sure how!
SomeBean oOffendingBean = getTheBadBeanSomehow();
InvalidValue[] badVals = oValidator.getInvalidValues(oOffendingBean);
}
}
So, not only am I choking on what the Spring AOP (pointcut definitions, etc.) configuration would look like to intercept the Hibernate Validator annotations I want, and not only do I not fully grasp how to implement the actual advice (e.g. how to instantiate the offending SomeBean from inside the advice as I mention above in the comments), but I'm not even sure if this solution is possible, Spring or otherwise.
Thanks in advance for some gentle "nudges" in the right direction!
You might be interested in the method validation feature introduced with Hibernate Validator 4.2 which provides support for validating method parameters and return values.
You then might use Seam Validation which integrates this functionality with CDI. If you want to use method validation together with Spring you could have a look this project on GitHub which shows how to integrate the method validation functionality with Spring AOP (disclaimer: I'm the author of this project as well as of Seam Validation).
To make your example working you would have to annote the parameter of the setter method with #NotEmpty like this:
public class SomeBean {
private String data;
#NotEmpty
public String getData() { return data; }
public void setData(#NotEmpty String data) { this.data = data; }
}