Related
In Spring data JPA there are annotations that can be used to set up validations for entities in a declarative manner. They can be found in javax.validation.constraints.* or additionally in org.hibernate.validator.constraints.* (in the case when Hibernate is plugged in).
Example:
#NotNull
private String lastName;
However, if the case of Spring data r2dbc they do not work out of the box.
Is there any simple and smooth way to set up validations for entities in Spring data r2dbc? That should not be too difficult in my opinion because probably it does not require full ORM support, just a matter of callbacks for checking object fields before persisting it.
In the RestController, validate the incoming request body using the annotations from Bean validation and Hibernate validators.
For RouterFunction or manal validation in your service, inject a Validator or ValidatorFactory to validate the request body before the data is (converted and) persisted into databases.
JPA is tightly integrated with Bean Validation/Hibernate Validator, besides validation, it will affect the generated schemes by default.
In a real world application, do not use the data layered entity classes in the web layer as request body class.
It works very fine with Bean Validation and Hibernate Validation.
You can use all the same annotations you did in JPA, since you do it in your DTOs.
See the example:
#Data
public class UserDto {
private String name;
#NotNull(message = "Last name can not be empty")
private String lastName;
#Min(value = 10, message = "Required min age is 10")
#Max(value = 50, message = "Required max age is 50")
private int age;
}
Then your controller would be annotated:
#RestController
#RequestMapping("user")
public class RegistrationController {
#Autowired
private UserService userService;
#PostMapping("register")
public Mono<UserDto> register(#Valid #RequestBody Mono<UserDto> userDtoMono{
return this.userService.registerUser(userDtoMono);
}
}
The only thing now is that you have to personalize your message, so that it returns the one you have set in your DTO. To override it you should create an ExceptionHandler.
#ControllerAdvice
public class ValidationHandler {
#ExceptionHandler(WebExchangeBindException.class)
public ResponseEntity<List<String>> handleException(WebExchangeBindException e) {
var errors = e.getBindingResult()
.getAllErrors()
.stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.toList());
return ResponseEntity.badRequest().body(errors);
}
}
I've been using it in my projects and got this example from: https://www.vinsguru.com/spring-webflux-validation/
Given below is my main controller from which I am calling the getPDFDetails method.
#RequestMapping(value=PATH_PRINT_CONTRACTS, method=RequestMethod.POST)
public ResponseEntity<?> printContracts(#RequestBody final UpdatePrintContracts updatePrintContracts) throws Exception {
System.out.println("contracts value is "+ updatePrintContracts);
Integer cancellationReasons = service.getPDFDetails(updatePrintContracts);
System.out.println("success!");
return ResponseEntity.ok(cancellationReasons);
}
Below is the UpdatePrintContracts class where I have defined all the variables with validation annotations and corresponding getter/setter methods.
public class UpdatePrintContracts {
#Valid
#NotBlank
#Pattern(regexp = "\\p{Alnum}{1,30}")
String isReprint;
#Valid
#NotBlank
Integer dealerId;
#Valid
#NotBlank
#Pattern(regexp = "\\p{Alnum}{1,30}")
String includeSignatureCoordinates;
#Valid
#NotBlank
java.util.List<Integer> contractNumbers;
public String getIsReprint() {
return isReprint;
}
public void setIsReprint(String isReprint) {
this.isReprint = isReprint;
}
public Integer getDealerId() {
return dealerId;
}
public void setDealerId(Integer dealerId) {
this.dealerId = dealerId;
}
public String getIncludeSignatureCoordinates() {
return includeSignatureCoordinates;
}
public void setIncludeSignatureCoordinates(String includeSignatureCoordinates) {
this.includeSignatureCoordinates = includeSignatureCoordinates;
}
public java.util.List<Integer> getContractNumbers() {
return contractNumbers;
}
public void setContractNumbers(java.util.List<Integer> contractNumbers) {
this.contractNumbers = contractNumbers;
}
}
I am trying to run the application as a Spring Boot app by right clicking on the project (Run As) and passing blank values for variables isReprint and includeSignatureCoordinates through Soap UI. However the validation doesn't seem to work and is not throwing any validation error in Soap UI. What am I missing? Any help is appreciated!
If you are facing this problem in latest version of spring boot (2.3.0) make sure to add the following dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Observation:
In earlier version of Spring Boot (1.4.7), javax.validation used to work out of the box. But, after upgrading to latest version, annotations broke. Adding the following dependency alone doesn't work:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
Because this provides JSR Specification but not the implementation. You can also use hibernate-validator instead of spring-boot-starter-validation.
For Anyone who is getting this issue with 2.0.1.Final:
In all SpringBoot versions above 2.2, Validations starter is not a part of web starter anymore
Check Notes here
So, all you have to do is add this dependency in your build.gradle/pom file
GRADLE:
implementation 'org.springframework.boot:spring-boot-starter-validation'
MAVEN
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
First you dont need to have #Valid annotation for those class variables in UpdatePrintContracts . You can delete them.
To trigger validation of a #Controller input, simply annotate the input argument as #Valid or #Validated:
#RequestMapping(value=PATH_PRINT_CONTRACTS, method=RequestMethod.POST)
public ResponseEntity<?> printContracts(#Valid #RequestBody final UpdatePrintContracts updatePrintContracts) throws Exception {
Refer here for full understanding of validating models in spring boot.
And If you want to check that a string contains only specific characters, you must add anchors (^ for beginning of the string, $ for end of the string) to be sure that your pattern matches all the string.Curly brackets are only to write a quantity,
#Pattern(regexp = "^[\\p{Alnum}]{1,32}$")
Lastly i assume you have following jars in your classpath,
.validation-api.jar (contains the abstract API and the annotation scanner)
.hibernate-validator.jar (contains the concrete implementation)
I faced the same error.
I had to use the below 2 dependencies alone:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
And use #Validated annotation(import org.springframework.validation.annotation.Validated) on rest controller level and #Valid annotation at method argument level(import javax.validation.Valid)
If there are any other extra dependencies like javax.validation.validation-api, org.hibernate.hibernate-validator, etc then the validations stopped working for me. So make sure that you remove these dependencies from pom.xml
I was using This dependency of validation in spring boot and didn't work ,
<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
I replaced it with spring-boot-starter-validation and it worked .
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-
starter-validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.4.0</version>
this is for anyone here who still has the same issue after following the steps mentioned above. I had to restart my IDE (IntelliJ) for the changes to take effect.
My problem solved by this.
When we use classes inside classes that also need validations so #Valid needs to be annotated to all in that case.
Link for more details
Make sure to use #Valid annotation before #RequestBody
For newer versions of spring boot ensure all validation annotation are picked from jakarta.validation.* package and not javax.validation.*. As the annotations are named same in both.
Step-1: Add these two dependency in the pom.xml file
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Step-2: Create a Custom Exception class like this
package com.bjit.salon.auth.service.exceptions;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
import java.util.stream.Collectors;
#ControllerAdvice
public class AnynameApplicationException {
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseBody
public ResponseEntity<List<String>> processUnmergeException(final
MethodArgumentNotValidException ex) {
List<String> list = ex.getBindingResult().getAllErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.toList());
return new ResponseEntity<>(list, HttpStatus.BAD_REQUEST);
}
}
Step-3: Add #Valid annotation to the method arguments like this way
public ResponseEntity<?> registerAccount(#Valid #RequestBody UserRegisterDto
registerDto) {
// rest of the codes
}
You have to add this dependency in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Note: SNAPSHOT, M1, M2, M3, and M4 releases typically WORK IN PROGRESS. The Spring team is still working on them, Recommend NOT using them.
You can use #NotEmpty will check for both blank and null values.
Add #Valid to your RestContoller class methods
I am building a REST API. Its made up of a Resource ( #Controller ) which returns a response 204 even when one of the mandatory field is not present.
I am using Spring 3.1, validation-api (1.1.0.Final) & Hibernate-validator(4.3.0). Not sure if Hibernate-validator plays any role here.
<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>4.3.0.Final</version>
<exclusions>
<exclusion>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</exclusion>
</exclusions>
</dependency>
I have a spring controller #Controller and a Bean with #Component
#POST
#Consumes(value = {MediaType.APPLICATION_JSON})
#Produces(value = {MediaType.APPLICATION_JSON})
#RequestMapping(method = RequestMethod.POST)
public Response addUserData(#Valid #RequestBody UserData userDataRequest) throws ResourceException {
...
}
My UserData bean has
#Component
public class UserData {
#NotNull(message = "user ID should not be null")
#Min(value = 1, message = "user ID should not be empty")
#Max(value = 20, message = "user ID should have length of more than 20")
#Pattern(regexp="[A-Z][a-z]+", message = "Only Alphanumeric characters allowed")
private String userId;
}
My validations are not getting executed. When I dont pass "userId", there is no error thrown. What am I missing here ?
You must have the following about the infrastructure
#EnableWebMvc
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
...
#Autowired
private ValidatorConfig validatorConfig;
...
#Override
public Validator getValidator() {
return validatorConfig.localValidatorFactoryBean();
}
...
}
Where validatorConfig comes from
#Configuration
public class ValidatorConfig {
#Autowired
private ReloadableResourceBundleMessageSource rrbms;
#Bean
public LocalValidatorFactoryBean localValidatorFactoryBean(){
LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
localValidatorFactoryBean.setValidationMessageSource(rrbms);
return localValidatorFactoryBean;
}
}
And finally (I suggest you consider put the error messages in a .properties file, such as validation.properties how shown below)
#Configuration
public class MessageSourceConfig {
#Bean(name="messageSource")
public ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource() {
ReloadableResourceBundleMessageSource resource = new ReloadableResourceBundleMessageSource();
resource.setBasenames("classpath:/com/manuel/jordan/somethinga",
"classpath:/com/manuel/jordan/validation/validation");
resource.setDefaultEncoding("UTF-8");
return resource;
}
}
Some considerations or suggestions:
change #Valid to #Validated (see the API for the #Validated)
Remove the #Component for UserData (that represents a kind of entity right?). Remember that for that class each instance is unique and any bean managed by Spring is Singleton.
put the error messages in a .properties file
from where come the #POST, #Consumes and #Produces annotations?. They are not in the Spring API
Addition 01 about your comment:
Yes, you must use #EnableWebMVC. It indicates to Spring create some special beans internally for the web environment. See the #EnableWebMVC API. Is very important that annotation. Even for Rest I use that annotation.
About the Rest annotations, use the Spring annotations. Such as #RequestMapping and new 'variations' such as #GetMapping, #PostMapping etc.. That annotations contain the produces and consumes attributes. I have never seen your approach about mixing two sets of annotations from Rest.
Addition 02
The WebMvcConfigurerAdapter class represents the XML configuration file about all the Spring MVC infrastructure
Therefore for XML
#EnableWebMvc is equivalent <mvc:annotation-driven/>
About validation it should be: <mvc:annotation-driven validator="beanNameValidator" /> where the validator attribute according with the .xsd says:
Attribute : validator The bean name of the Validator that is to be
used to validate Controller model objects. This attribute is not
required, and only needs to be specified explicitly if a custom
Validator needs to be configured. If not specified, JSR-303
validation will be installed if a JSR-303 provider is present on the
classpath.
beanNameValidator according with my #Bean seems should be localValidatorFactoryBean
I ultimately ended up using Jersey Bean Validation, instead of Spring. This is because rest of my code is using Jersey anyways. To make this work I just imported Jersey Bean Validation jar and added a small change to web.xml. Validations are now working.
Thank you #Manual Jordan. I will upvote your answer, since it gave me the right clues.
<!-- jersey beanValidation -->
<init-param>
<param-name>jersey.config.beanValidation.enableOutputValidationErrorEntity.server</param-name>
<param-value>true</param-value>
</init-param>
Maybe passing to your "addUserData" method, a bindingResult Object, so you can test for and retrieve validation errors.
here is an example of how to use it : Validation form input
Spring supports two different validation methods: Spring validation and JSR-303 bean validation. Both can be used by defining a Spring validator that delegates to other delegators including the bean validator. So far so good.
But when annotating methods to actually request validation, it's another story. I can annotate like this
#RequestMapping(value = "/object", method = RequestMethod.POST)
public #ResponseBody TestObject create(#Valid #RequestBody TestObject obj, BindingResult result) {
or like this
#RequestMapping(value = "/object", method = RequestMethod.POST)
public #ResponseBody TestObject create(#Validated #RequestBody TestObject obj, BindingResult result) {
Here, #Valid is javax.validation.Valid, and #Validated is org.springframework.validation.annotation.Validated. The docs for the latter say
Variant of JSR-303's Valid, supporting the specification of validation
groups. Designed for convenient use with Spring's JSR-303 support but
not JSR-303 specific.
which doesn't help much because it doesn't tell exactly how it's different. If at all. Both seem to be working pretty fine for me.
A more straight forward answer.
For those who still don't know what on earth is "validation group".
Usage for #Valid Validation
Controller:
#RequestMapping(value = "createAccount")
public String stepOne(#Valid Account account) {...}
Form object:
public class Account {
#NotBlank
private String username;
#Email
#NotBlank
private String email;
}
Usage for #Validated Validation Group
Source: http://blog.codeleak.pl/2014/08/validation-groups-in-spring-mvc.html
Controller:
#RequestMapping(value = "stepOne")
public String stepOne(#Validated(Account.ValidationStepOne.class) Account account) {...}
#RequestMapping(value = "stepTwo")
public String stepTwo(#Validated(Account.ValidationStepTwo.class) Account account) {...}
Form object:
public class Account {
#NotBlank(groups = {ValidationStepOne.class})
private String username;
#Email(groups = {ValidationStepOne.class})
#NotBlank(groups = {ValidationStepOne.class})
private String email;
#NotBlank(groups = {ValidationStepTwo.class})
#StrongPassword(groups = {ValidationStepTwo.class})
private String password;
#NotBlank(groups = {ValidationStepTwo.class})
private String confirmedPassword;
}
As you quoted from the documentation, #Validated was added to support "validation groups", i.e. group of fields in the validated bean. This can be used in multi step forms where you may validate name, email, etc.. in first step and then other fields in following step(s).
The reason why this wasn't added into #Valid annotation is because that it is standardized using the java community process (JSR-303), which takes time and Spring developers wanted to allow people to use this functionality sooner.
Go to this jira ticket to see how the annotation came into existence.
In the example code snippets of the question, #Valid and #Validated make no difference. But if the #RequestBody is annotated with a List object, or is a string value annotated by #RequestParam, the validation will not take effect.
We can use the #Validated's method-level validation capability to make it work. To achieve this, the key point is to place #Validated on the class. This may be another important difference between #Valid and #Validated in spring framework.
Refrence
Spring boot docs
Just for simplifying:
#Validated annotation is a class-level annotation that we can use to tell Spring to validate parameters that are passed into a method of the annotated class.
and
#Valid annotation on method parameters and fields to tell Spring that we want a method parameter or field to be validated.
besides above, you can only apply #Valid on a domain/field for nested validation, not with a #validated.
#Validated can be used for a class:
#Validated
public class Person {
#Size(min=3)
private String name;
...
What configuration is needed to use annotations from javax.validation.constraints like #Size, #NotNull, etc.? Here's my code:
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class Person {
#NotNull
private String id;
#Size(max = 3)
private String name;
private int age;
public Person(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
}
When I try to use it in another class, validation doesn't work (i.e. the object is created without error):
Person P = new Person(null, "Richard3", 8229));
Why doesn't this apply constraints for id and name? What else do I need to do?
For JSR-303 bean validation to work in Spring, you need several things:
MVC namespace configuration for annotations: <mvc:annotation-driven />
The JSR-303 spec JAR: validation-api-1.0.0.GA.jar (looks like you already have that)
An implementation of the spec, such as Hibernate Validation, which appears to be the most commonly used example: hibernate-validator-4.1.0.Final.jar
In the bean to be validated, validation annotations, either from the spec JAR or from the implementation JAR (which you have already done)
In the handler you want to validate, annotate the object you want to validate with #Valid, and then include a BindingResult in the method signature to capture errors.
Example:
#RequestMapping("handler.do")
public String myHandler(#Valid #ModelAttribute("form") SomeFormBean myForm, BindingResult result, Model model) {
if(result.hasErrors()) {
...your error handling...
} else {
...your non-error handling....
}
}
You should use Validator to check whether you class is valid.
Person person = ....;
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
Set<ConstraintViolation<Person>> violations = validator.validate(person);
Then, iterating violations set, you can find violations.
In my case, I was using spring boot version 2.3.0. When I changed my maven dependency to use 2.1.3 it worked.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
</dependencies>
I come here some years after, and I could fix it thanks to atrain's comment above. In my case, I was missing #Valid in the API that receives the Object (a POJO in my case) that was annotated with #Size. It solved the issue.
I did not need to add any extra annotation, such as #Valid or #NotBlank to the variable annotated with #Size, just that constraint in the variable and what I mentioned in the API...
Pojo Class:
...
#Size(min = MIN_LENGTH, max = MAX_LENGTH);
private String exampleVar;
...
API Class:
...
public void exampleApiCall(#RequestBody #Valid PojoObject pojoObject){
...
}
Thanks and cheers
You would have to call a Validator on the Entity if you want to validate it. Then you will get a set of ConstraintViolationException, which basically show for which field/s of your Entity there is a constraint violation and what exactly was it. Maybe you can also share some of the code you expect to validate your entity.
An often used technique is to do validation in #PrePersist and rollback transaction if using multiple data modifications during transaction or do other actions when you get a validation exception.
Your code should go like this:
#PrePersist
public void prePersist(SomeEntity someEntity){
Validator validator = Validation.buildDefaultValidatorFactory.getValidator();
Set<ConstraintViolation<SomeEntity>> = validator.validate(someEntity);
//do stuff with them, like notify client what was the wrong field, log them, or, if empty, be happy
}
In my case the reason was the hibernate-validator version.
Probably something is not supported in the newer version any more.
I changed:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version>
</dependency>
I downgraded the version from 7.0.1.Final to 6.0.2.Final and this helped me.
You can also simply use #NonNull with the lombok library instead, at least for the #NotNull scenario. More details: https://projectlombok.org/api/lombok/NonNull.html
After the Version 2.3.0 the "spring-boot-strarter-test" (that included the NotNull/NotBlank/etc) is now "sprnig boot-strarter-validation"
Just change it from ....-test to ...-validation and it should work.
If not downgrading the version that you are using to 2.1.3 also will solve it.
You need to add #Valid to each member variable, which was also an object that contained validation constraints.
in my case i had a custom class-level constraint that was not being called.
#CustomValidation // not called
public class MyClass {
#Lob
#Column(nullable = false)
private String name;
}
as soon as i added a field-level constraint to my class, either custom or standard, the class-level constraint started working.
#CustomValidation // now it works. super.
public class MyClass {
#Lob
#Column(nullable = false)
#NotBlank // adding this made #CustomValidation start working
private String name;
}
seems like buggy behavior to me but easy enough to work around i guess
I also faced the same problem. Javax annotations ( #NotNull, #Valid) were not performing any validation. Their presence was not making any difference.
I have to use 'springboot-starter-validation' dependency to make the javax validations effective.
Here is the related dependencies configuration. Also don't miss to add #Valid annotation on the Object you want to validate.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
.....
.....
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependencies>
Recently I faced the same. I upgraded hibernate-validator to ver 7.x but later I noticed this release note
Hibernate Validator 7.0 is the reference implementation for Jakarta Bean Validation 3.0.
The main change is that all the dependencies using javax. packages are now using jakarta.* packages.
Upgrade to Hibernate Validator 7 is only recommended if you are moving to Jakarta EE 9.
My project should target java 8, so keeping javax.validation instead of switiching to jakarta.validation, I've had to downgrade to
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.2.Final</version>
</dependency>
So #Valid at service interface would work for only that object. If you have any more validations within the hierarchy of ServiceRequest object then you might to have explicitly trigger validations. So this is how I have done it:
public class ServiceRequestValidator {
private static Validator validator;
#PostConstruct
public void init(){
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
public static <T> void validate(T t){
Set<ConstraintViolation<T>> errors = validator.validate(t);
if(CollectionUtils.isNotEmpty(errors)){
throw new ConstraintViolationException(errors);
}
}
}
You need to have following annotations at the object level if you want to trigger validation for that object.
#Valid
#NotNull
for method parameters you can use Objects.requireNonNull() like this:
test(String str) {
Objects.requireNonNull(str);
}
But this is only checked at runtime and throws an NPE if null. It is like a preconditions check. But that might be what you are looking for.
Great answer from atrain,
but maybe better solution to catch exceptions is to utilize own HandlerExceptionResolver
and catch
#Override
public ModelAndView resolveException(
HttpServletRequest aReq,
HttpServletResponse aRes,
Object aHandler,
Exception anExc
){
// ....
if(anExc instanceof MethodArgumentNotValidException) // do your handle error here
}
Then you're able to keep your handler as clean as possible.
You don't need BindingResult, Model and SomeFormBean in myHandlerMethod anymore.
I came across this problem recently in a very similar situation:
Met all requirements as the top-rated answer listed but still got the wrong result.
So I looked at my dependencies and found I was missing some of them. I corrected it by adding the missing dependencies.
I was using hibernate, the required dependencies were:
*Snapshot taken in class "Spring & Hibernate for Beginners" # Udemy
If you are using lombok then, you can use #NonNull annotation insted.
or Just add the javax.validation dependency in pom.xml file.
For those who have not been able to perform server-side validation through Hibernate validation dependency.
Just remove Hibernate validator +javax validation dependency and add spring-boot-starter validation. It provides Hibernate Validator Internally, and it worked just fine for me.
Credits:- a comment from youtube.
By default javax validation in spring works for Rest controller method input variables. But for other places to use the same we have to annotate class containing #Valid annotation with #Validated class level annotation.
I was facing same issue with kafka listener and after that I annotated it with #Validated it started working.
#Component
#Log4j2
#Validated
public class KafkaMessageListeners {
#KafkaListener(topics = "message_reprocessor", errorHandler = "validationErrorHandler")
public void processMessage(#Payload #Valid CustomPojo payload,
#Header(KafkaHeaders.OFFSET) List<Long> offsets, Acknowledgment acknowledgment) {
}
}
I fell into the same issue, but I got solution
your servlet configuration xml file i.e {servlet-name}-servlet.xml file
should be like
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package = "spring.tutorial.controller" />
->>> Step 4: Add support for conversion, formatting and validation support
<mvc:annotation-driven/>
<bean class = "org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name = "prefix" value = "/WEB-INF/views/" />
<property name = "suffix" value = ".jsp" />
</bean>
</beans>
step 4 is important one
Just add the following in your pom.xml under tag if you are using Maven.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
For Gradle you can do the following.
dependencies {
...
implementation 'org.springframework.boot:spring-boot-starter-validation'
}
In my case i removed these lines
1-import javax.validation.constraints.NotNull;
2-import javax.validation.constraints.Size;
3- #NotNull
4- #Size(max = 3)