I want to validate extension of Multipartfile object. I added #Valid and my custon annotation to parameter #ImageFileValid but it doesn't work.
#PutMapping("/{id}")
ProductDto updateProduct(#RequestPart #Valid ProductDto product, #PathVariable Long id,#RequestPart #Valid #ImageFileValid MultipartFile image) {
return productMapper.productToProductDto(productService.update(productMapper.productDtoToProduct(product),id));
}
Very short but clear reference from Spring-Boot, Validation:
The method validation feature supported by Bean Validation 1.1 is automatically enabled as long as a JSR-303 implementation (such as Hibernate validator) is on the classpath. This lets bean methods be annotated with javax.validation constraints on their parameters and/or on their return value. Target classes with such annotated methods need to be annotated with the #Validated annotation at the type level for their methods to be searched for inline constraint annotations.
So, please annotate (the containing) controller class with #Validated & report if any issues.
A sample repo at github.
Related
With the following validation setup in an annotated MVC controller:
#RestController
#RequestMapping("/users")
#Validated // <-- without this, the #Size annotation in setPassword() has no effect
public class UserController {
#PutMapping("/{id}/password")
public void setPassword(#PathVariable long id, #RequestBody #Size(min = 8) String password) {
/* ... */
}
#PutMapping("/{id}/other")
public void setOther(#PathVariable long id, #RequestBody #Valid MyFormObject form) {
/* ... */
}
}
#Validated on the controller is required for the method parameter since it's not a "complex" object. In comparison, the #Valid annotation on the setOther method works without the #Validated annotation.
Why is #Validated required? Why not enable it by default? Is there a cost to its use?
edit
Note that Difference between #Valid and #Validated in Spring is related (I read it before asking this), but it doesn't address the why in my question.
Validation of objects is done by Hibernate Validator using the annotations from Jakarta Bean Validation 2.0. Something needs to trigger hibernate validator to run.
SpringMVC calls the controller methods when it sees a parameter with #Valid it will pass that object to hibernate validator. Hibernate validator will
Examine the class of the object to figure out what validation rules have been put on the class fields
Execute the validation rules against the fields marked up with validation annotations.
So in this case
#PutMapping("/{id}/other")
public void setOther(#PathVariable long id, #RequestBody #Valid MyFormObject form) {
/* ... */
}
MyFormObject has annotations on it that the hibernate validator can find to validate the object.
In this case
#PutMapping("/{id}/password")
public void setPassword(#PathVariable long id, #RequestBody #Size(min = 8) String password) {
/* ... */
}
java.lang.String does not have any annotations defined on it for hibernate validator to discover.
#Valid comes from the Bean validation package javax.validation.Valid while #Validated comes from Spring org.springframework.validation.annotation.Validated
#Validated annotation activates the Spring Validation AOP interceptor and it will examine method parameters to see if they have any validation annotations on them, if they do then Spring will call hibernate validator with each specific annotation for example #Size(min = 8) String password means call hibernate size validator and pass the value of the parameter password in this case hibernate validator does not need to scan java.lang.String to see if it has validation annotations on it. #Validated works on any spring #Component you can use it on #Service classes for example.
There is extra overhead for using #Validated similar to using #Transactional so that is why you have to opt into it. In the case of javax.validation.Valid Spring MVC needs to check the annotations on the controller method parameters so when it sees #Valid it is easy for it to send that object the Hibernate Validator without needing to add an AOP interceptor.
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.
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 am developing a service(not a web application) using Spring-3.1.0.GA. I want to use hibernate-validator along with Spring to validate my service inputs.
I have enabled the bean validation support with:
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
I have annotated my service interface with #Validated and method parameters with #NotNull, #Size, #Valid etc and it is working fine.
But I have to validate my parameter, say Customer, based on validation group(s).
If I annotate method parameter with #Validated or #Validated(Group1.class) Spring is not performing any validation.
If I annotate with #Valid then validations are happening but with Default validation group(as expected).
I can't annotate #Validate(Group1.class) at interface level because various methods operate on various groups.
How can I perform service layer validations using Spring and hibernate-validator with groups support?
I have gone through Spring Source code and why the validation groups on method params are not being picked...As per the code it is picking the validation groups configuration only from #Validated on interface level, not on method level.
But I haven't gone through how it is working in SpringMVC context, I haven't checked yet.
Also in this process I came to know If you configure MethodValidationPostProcessor in child application context it is not picked up by Spring. I mean if you are using SpringMVC, MethodValidationPostProcessor will be registered only if you configure it in [servlet-name]-servlet.xml. If you configure in any child context configuration files that are picked by ContextLoaderListener Spring won't register MethodValidationPostProcessor. Also I am not sure it is only for MethodValidationPostProcessor or for any BeanPostProcessors also.
I get the following code working, I need to attach the #Validated at the method header and #Valid in the method parameters.
#Validated(Default.class)
public UserDto updateUser(#Valid UserDto userDto) {
In Spring 3.1.0+ you can use groups directly in #Valid annotation. E.g.:
#Valid(groups={Default.class, Group1.class})
I have seen that annotation like #NotNull etc are given in JSR Specifications.
Now as i am currently studying Spring MVC and building website in that
so i am confused are they same or different and have they any relation with spring MVC.
because for .eg if i use #NotEmpty then will spring knows that if i leave it empty then it displays in form with error message like we code in validator and its messages
This is my method , i am confused were to add #Valid
public String add(#ModelAttribute("personAttribute") Person person) {
logger.debug("Received request to add new person");
personService.add(person);
// This will resolve to /WEB-INF/jsp/addedpage.jsp
return "hibernate/addedpage";
}
Spring form validation is different, but it also support JSR-303 validation. You can validate a whole model attribute by annotating it with #Valid (as a method parameter)
public String add(#Valid #ModelAttribute("personAttribute") Person person) { .. }
You would need:
a JSR-303 provider on the classpath
<mvc:annotation-driven /> in the spring-mvc xml to enable validation