2. Background
Please refer my earlier presentation to get basics,
https://www.slideshare.net/MohammadSabirKhan/spring-rest-request-validation
Part -1 presentation highlights use of @Valid annotation
With @Valid annotation, you can validate Java POJOs i.e. specific to Spring REST, it
would be @RequestBody
If you simply place @Valid & @NotEmpty to a GET request @RequestParam or
@PathVariable, it wouldn’t work i.e. it will have no effect – validator wouldn’t be
invoked
Directly placing JSR annotations for method parameters was not supported in bean
validation 1.0 ( JSR – 303 ) and support started from bean validation 1.1 ( JSR –
349)
So you have to make sure that you are using JSR – 349 implementation before using
this feature
3. Why we need it?
For a REST End Point – Its not guaranteed that client will always send a well formed
request
REST Entry Point need not to proceed if request is invalid and data sent is improper
If request is invalid, REST Entry Point need to return an error response automatically and
service developers need not be tweaking service logic for data invalidity
Validation needs to be segregated system component for maintainable flow and
readable code
As described in previous slide, you will have to convert method parameters into a POJO
to validate with @Valid, we are trying to avoid it and directly process validation
annotations placed on method signatures for simple arguments like String etc.
Lots of confusion is out there on Internet because of change of bean validation
standards, types of validations supported and various techniques to invoke validator
4. Getting Started : Coding…Dependency
First you need to include validation API standard and implementations in your REST application
that supports JSR – 349.
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.3.4.Final</version>
</dependency>
5. Getting Started : Coding…Dependency…Contd
Out of those two dependencies, validation-api-1.1.0.Final.jar is a reference to JSR –
349 while hibernate-validator-5.3.4.Final.jar is a JSR-349 implementation
Hibernate JAR has nothing to do with hibernate ORM implementation, its simply a
bean validation implementation and can be used in non – hibernate environments.
Here we are trying to use for Spring REST service.
If you are using Spring Boot, these two dependencies will already be there as part of
below dependency and not required to include separately.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
6. Coding…Enable Method Validation
Processing validation annotations as part of method signatures are not
enabled by default.
You need to tell Spring Framework to process @Email, @NotNull,
@NotEmpty annotations included in method signatures directly without
@Valid annotation
This is achieved in two steps,
1. Place @Validated -
org.springframework.validation.annotation.Validated at your
controller class, like ,
@RestController
@RequestMapping("/…")
@Validated
7. Coding…Enable Method Validation…Contd
2. Place these two beans in your Spring Configuration i.e. in
@Configuration class available for application
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor mvProcessor = new MethodValidationPostProcessor();
mvProcessor.setValidator(validator());
return mvProcessor;
}
@Bean
public LocalValidatorFactoryBean validator(){
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setProviderClass(HibernateValidator.class);
validator.afterPropertiesSet();
return validator;
}
8. Coding…Enable Method Validation…Contd
You have to note that Spring has its own validators and we have to somehow tell
Spring that method level annotation processing is enabled and which validator
factory to use to process those validations ( Its Hibernate implementation in our
case )
Hibernate validator documentation says that you can invoke their validator via some
AOP kind of mechanism and Spring provides that mechanism , refer JIRA -
https://jira.spring.io/browse/SPR-8199 to know a bit more
In Absence of this in build mechanism, you will have to invoke validator on your own
as described in - https://github.com/gunnarmorling/methodvalidation-integration
Anyway, now you are set to validate your @RequestParam & @PathVariable directly
in method signatures
9. Coding…Validate
Now , you can do below
@RequestMapping(method = RequestMethod.GET, value = "/testValidated" , consumes=MediaType.APPLICATION_JSON_VALUE, produces
=MediaType.APPLICATION_JSON_VALUE )
public ResponseBean<String> testValidated( @Email(message="email RequestParam is not a valid email address") @NotEmpty(message="email
RequestParam is empty") @RequestParam("email") String email ){
ResponseBean<String> response = new ResponseBean<>();
……
return response;
}
Above code tells that for @RequestParam(“email”) – validate if it’s a valid email address and if its not empty String
You can externalize message Strings
No need to place @Valid or @Validated in method signature
You can use other available annotations as per your need
10. Coding…Exception Handler
In part – 1 of presentation, we saw that @Valid throws – MethodArgumentNotValidException but @Validated throws a different exception –
ConstraintViolationException so we will have to write a handler for this exception too.
@RestControllerAdvice(value=“*.controller") -> this is basically controller package location
public class ApplicationExceptionHandler {
@ExceptionHandler
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseBean handle(ConstraintViolationException exception){
StringBuilder messages = new StringBuilder();
ResponseBean response = new ResponseBean();
int count = 1;
for(ConstraintViolation<?> violation:exception.getConstraintViolations()){
messages.append(" "+count+"."+violation.getMessage());
++count;
}
response.setResponse(Constants.FAILURE);
response.setErrorcode(Constants.ERROR_CODE_BAD_REQUEST);
response.setMessage(messages.toString());
return response;
}
}
ResponseBean is my application specific class that I wrote my own , you can have your own.
This handler will automatically be called if validation fails and response returned to your client.