can #Email and spring custom validation coexists? - spring

I need to validate Email and password while creating an account using web services. I am using Spring REST controller and plan to use #Email(hibernate validator) annotation for email id validation.For Password validation I have to write custom validator by implementing Validator interface.
#XmlRootElement
public class Account implements Serializable {
#Email(message =AccountErrorCodes.ACCOUNT_EMAIL_VALIDATION)
private String emailId;
private String password;
For password writing the own validator
#Autowired
private PasswordValidator passwordValidator;
#InitBinder
private void initBinder(WebDataBinder binder) {
binder.setValidator(passwordValidator);
}
It looks like both cant coexist. When I try to validate EmailId spring calls passwordValidator but fails to validate Email. I expect the failure due to incorrect Email id. When I disable the custom validator I get the required error message.
Looks like I am missing something. Do I need to do something to make it work? or it is not supported? If not supported Can I call Hibernate validator directly from spring custom validator to avoid writing my own validation for email?

Instead of injecting your custom validator in your controller, make a special #Password annotation for it that uses this as a validator. Remove any constraint logic from controller and do it all on your DTO class. Something like this:
#Target({ METHOD, FIELD, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Constraint(validatedBy = PasswordValidator.class)
public #interface Password {
String message() default "{Invalid password}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
You will find loads of tutorials for custom annotations that use your own made validators. Hope that helps.

You asked :
By Writing our own custom annotations can I still use Binding Result object that Spring provides which returns multiple error messages?
Yes .
You can make your own custom annotations for any type .
For example :
Phone Number
#Phone
#NotEmpty
private String phone;
Now make a java file :
Phone.java
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
import javax.validation.Constraint;
import javax.validation.Payload;
#Documented
#Constraint(validatedBy = PhoneValidator.class)
#Target( { ElementType.METHOD, ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
public #interface Phone {
String message() default "{Phone}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And a class to provide validations measures :
PhoneValidator.java
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class PhoneValidator implements ConstraintValidator<Phone, String> {
#Override
public void initialize(Phone paramA) {
// TODO Auto-generated method stub
}
#Override
public boolean isValid(String phoneNo, ConstraintValidatorContext ctx) {
// TODO Auto-generated method stub
if(phoneNo == null) {
return false;
}
//Validate phone numbers of format "1234567890"
if (phoneNo.matches("\\d{10}")) return true;
//validating phone number with -, . or spaces
else if(phoneNo.matches("\\d{3}[-\\.\\s]\\d{3}[-\\.\\s]\\d{4}")) return true;
//validating phone number with extension length from 3 to 5
else if(phoneNo.matches("\\d{3}-\\d{3}-\\d{4}\\s(x|(ext))\\d{3,5}")) return true;
//validating phone number where area code is in braces ()
else if(phoneNo.matches("\\(\\d{3}\\)-\\d{3}-\\d{4}")) return true;
//return false if nothing matches the input
else return false;
}
}
Now in :
message_en.properties
Include :
#Custom validation annotation
Phone=Invalid format, valid formats are 1234567890, 123-456-7890 x1234
By this way , you will succesfully get a custom validator .
BindingResult
will catch the error and get the message from the properties file .
Hope this helps .

Related

How can I create custom validator on Java List type?

I have one NumberConstraint as follows:
#Constraint(validatedBy = { StringConstraintValidator.class, })
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD, ElementType.PARAMETER, })
public #interface StringConstraint {
String message() default "'${validatedValue}' ist not valid Number. " +
"A String is composed of 7 characters (digits and capital letters). Valid example: WBAVD13.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
and it is validated by StringConstraintValidator as follows:
#Component
public class StringConstraintValidator implements ConstraintValidator<StringConstraint, String> {
private static final String REGEX_String = "^[A-Z0-9]{7}$";
#Override
public void initialize(final StringConstraint annotation) {
// noop
}
#Override
public boolean isValid(final String value, final ConstraintValidatorContext context) {
return isValid(value);
}
public boolean isValid(final String value) {
// Number is not always null Checked via #NotNull
LoggingContext.get().setString(value);
if (value == null) {
return false;
}
return Pattern.compile(REGEX_STRING).matcher(value).matches();
}
}
I can apply this StringConstraint on single field of request object as follows:
#StringConstraint
private String number;
but if my request object contains a List of Strings then how can I use this constraint on entire List or do I have to define new on List type ?? Something like ConstraintValidator<StringConstraint, List ???>
My request object is:
#JsonProperty(value = "items", required = true)
#Schema(description = "List of items.", required = true)
private List<String> items= new ArrayList<>();
So i want to apply my validator on all the strings in the list. How can i apply #StringConstraint on my list ?
Yes, you can add more validators for one constraint by using a comma-separated list of validator classes in the validatedBy attribute of the #Constraint annotation. For example, you can write:
#Constraint(validatedBy = {StringConstraintValidator.class, BlablaValidot.class})
public #interface MyConstraint {
// other attributes
}
Explanation
The #Constraint annotation is used to define a custom constraint annotation that can be applied to fields, methods, classes, etc. The validatedBy attribute specifies one or more classes that implement the ConstraintValidator interface and provide the logic to validate the annotated element. You can use multiple validators for the same constraint if you want to check different aspects or conditions of the value. For example, you can have one validator that checks the length of a string and another that checks the format of a string.
Examples
Here are some examples of custom constraint annotations with multiple validators:
A #PhoneNumber annotation that validates a phone number using two validators: one for the country code and one for the number format.
#Constraint(validatedBy = {CountryCodeValidator.class, PhoneNumberValidator.class})
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
public #interface PhoneNumber {
String message() default "Invalid phone number";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
A #Password annotation that validates a password using three validators: one for the minimum length, one for the maximum length, and one for the presence of special characters.
#Constraint(validatedBy = {MinLengthValidator.class, MaxLengthValidator.class, SpecialCharValidator.class})
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
public #interface Password {
String message() default "Invalid password";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
int minLength() default 8;
int maxLength() default 20;
String specialChars() default "!##$%^&*";
}
A custom validator in Java is a way to define your own rules for validating the data of your objects or parameters. You can use the javax.validation API to create and use custom validators. The API consists of two main components: annotations and validators.
Annotations are used to declare the constraints that you want to apply on your data. They are usually placed on the fields or parameters that you want to validate. You can use the built-in annotations provided by the API, such as #NotNull, #Size, #Pattern, etc., or you can define your own annotations for your custom constraints.
Validators are classes that implement the ConstraintValidator interface and provide the logic for validating the data against the constraints. The interface has two generic parameters: A, which is the annotation type, and T, which is the data type. The interface has two methods: initialize and isValid. The initialize method is used to initialize the validator with the annotation attributes, and the isValid method is used to check if the data is valid or not according to the annotation.
To create a custom validator for a list type, you need to specify the list type as the second generic parameter of the ConstraintValidator interface, and implement the isValid method to iterate over the list elements and validate them individually. You can use any logic that suits your needs, such as checking the length, format, range, etc. of the elements. You can also use other annotations or validators inside your custom validator to reuse the existing validation rules.
Example
Here is an example of how to create a custom validator for a list of strings that checks if each element is a valid number.
Define the annotation
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
#Documented
#Constraint(validatedBy = StringConstraintValidator.class)
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
public #interface NumberConstraint {
String message() default "Invalid number";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Define the validator
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.List;
public class StringConstraintValidator implements ConstraintValidator<NumberConstraint, List<String>> {
#Override
public void initialize(NumberConstraint constraintAnnotation) {
// You can use this method to initialize the validator with the annotation attributes
}
#Override
public boolean isValid(List<String> value, ConstraintValidatorContext context) {
// You can use this method to validate the list elements
if (value == null || value.isEmpty()) {
return true; // You can change this to false if you want to reject null or empty lists
}
for (String s : value) {
try {
Double.parseDouble(s); // Try to parse the string as a double
} catch (NumberFormatException e) {
return false; // If the string is not a valid number, return false
}
}
return true; // If all the strings are valid numbers, return true
}
}
Apply the annotation
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.List;
public class Request {
#NotNull
#NumberConstraint
private List<String> numbers;
// Constructor, getters, setters, etc.
}
public class Controller {
public void processRequest(#Valid Request request) {
// Do something with the request
}
}
If you want to unit test the method
public void processRequest(#Valid Request request) {
// Do something with the request
}
with an invalid request, then the output should be an exception or an error message, depending on how you handle the validation.
Explanation
The #Valid annotation is used to indicate that the parameter should be validated before entering the method. This means that the request object should have some constraints or rules that define what makes it valid or invalid. For example, the request might have a required field, a maximum length, a specific format, etc.
If the request object does not meet these constraints, then the validation will fail and the method will not be executed. Instead, an exception will be thrown or an error message will be returned, depending on the implementation of the validation mechanism. This is to prevent the method from processing an invalid request that might cause unexpected behavior or errors.
Example
Suppose the Request class has the following constraints:
public class Request {
#NotNull
#Size(min = 1, max = 10)
private String name;
#Email
private String email;
// getters and setters
}
This means that the request object should have a non-null name that is between 1 and 10 characters long, and a valid email address. If we use the javax.validation API to perform the validation, then we can write a unit test like this:
import static org.junit.Assert.*;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import org.junit.Before;
import org.junit.Test;
public class RequestProcessorTest {
private RequestProcessor requestProcessor;
private Validator validator;
#Before
public void setUp() {
requestProcessor = new RequestProcessor();
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
#Test
public void testProcessRequestWithInvalidRequest() {
// create an invalid request object
Request request = new Request();
request.setName(""); // empty name
request.setEmail("invalid.com"); // invalid email
// validate the request object
Set<ConstraintViolation<Request>> violations = validator.validate(request);
// assert that there are violations
assertFalse(violations.isEmpty());
// try to process the request
try {
requestProcessor.processRequest(request);
fail("Should throw ConstraintViolationException");
} catch (ConstraintViolationException e) {
// assert that the exception contains the violations
assertEquals(violations, e.getConstraintViolations());
}
}
}

How to implement validation on 2 dependent fields?

Hi everyone iam writing post api in spring boot and i need to implement this validation
i have 2 fields - order type and additional details, order type is enum class which consists of 2 enum's - a and b , suppose if "a" is given as input it will proceed as it is - no validation required, suppose if it's "b" then additional details field must required, this is my requirement --- this is a post call
There are multiple ways to achieve that.
Firstly, javax.validation.constraints package has an #AssertTrue annotation, using which you can define a method in your class with some validation logic, e.g. like this:
#AssertTrue(message = "secondField must not be null if firstField is B")
public boolean isValid() {
return SomeEnum.A.equals(firstField) || secondField != null;
}
Secondly, you can define your own validation annotation - similar to how annotations are implemented in validation package - and a validator that implements ConstraintValidator. E.g. like this:
#ValidMyObject
public class MyObject {
private SomeEnum firstField;
private Object secondField;
// getters, setters, etc.
}
#Target({TYPE,ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = MyObjectValidator.class)
public #interface ValidMyObject {
String message() default "secondField must not be null if firstField is B";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
#Component
public class MyObjectValidator
implements ConstraintValidator<ValidMyObject, MyObject> {
#Override
public boolean isValid(MyObject obj, ConstraintValidatorContext context){
return SomeEnum.A.equals(obj.getFirstField()) || obj.getSecondField != null;
}
}
Note that the #Target annotation defines that this annotation can be used at a TYPE level - on a class, to perform the validation of the entire MyObject object.
Take a look here for details.
P.S. Don't forget about dependencies for validation implementation: for spring-boot the most popular one is 'org.springframework.boot:spring-boot-starter-validation'.

how to perform validations dynamically in spring?

I made a custom validation annotation for unique email (When user registers itself, program checks if email is already in database).
Everything works just fine, but when I need to modify user's info and not to create a new one I run into a problem that says "Email is already in use"
Can I somehow turn off only #EmailAvailablity validation(I need the others like email pattern and password validation)?
All validation annotations are in the same User bean.
These ARE MY CUSTOM VALIDATOR CLASS
User AVailablity
package com.vicky.finalYearProject.customValidations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
#Constraint(validatedBy = UserAvailabilityValidator.class)
#Target({ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface UserAvailability {
public String message() default "Email is already Registered";
public Class<?>[] groups() default { };
public Class<? extends Payload>[] payload() default { };
}
Constraint Validator
package com.vicky.finalYearProject.customValidations;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.vicky.finalYearProject.Service.CheckUserAvailabilityService;
public class UserAvailabilityValidator implements ConstraintValidator<UserAvailability, String>{
CheckUserAvailabilityService availabilityService = new CheckUserAvailabilityService();
#Override
public void initialize(UserAvailability constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
}
#Override
public boolean isValid(String email, ConstraintValidatorContext context) {
return availabilityService.isAvailable(email);//return true or false;
}
}
i have checked one question similar :
How to prevent Spring validation on update
but i cant understand it...
Thank You.
As this is a custom validator, you should consider adding the condition yourself, e.g. based on an ID which is only generated on save. For that purpose, making it a class-level constraint would be helpful, allowing you to check multiple values of your class.
Something like:
#Target(AnnotationTarget.CLASS)
#Retention(AnnotationRetention.RUNTIME)
#MustBeDocumented
#Constraint(validatedBy = [EmailAvailabilityValidator::class])
annotation class EmailAvailability(
val message: String = "Email must be available",
val groups: Array<KClass<*>> = [],
val payload: Array<KClass<out Any>> = []
)
class EmailAvailabilityValidator : ConstraintValidator<EmailAvailability, User> {
override fun isValid(value: User?, context: ConstraintValidatorContext?): Boolean {
if (value == null) return true
if (value.id != null) return true // don't execute it on saved entities
// your extra validation
}
}
Alternatively, when using Hibernate, here's a proposal on making validations only run on insert: Hibernate validations on save (insert) only
Besides that I would consider moving this check out of the bean. It sounds like you do any kind of database interaction in the validation. Coupling the model to the database breaks encapsulation and limits your test capabilities. Adding e.q. uniqueness to a column would be an alternative.

Use a ConstraintValidator to validate an object

I want to run this past my fellow Java hacks. See if this seems to be sane enough to you. There may be a better way to do this but I was thinking: Can I write a dual-purpose ConstraintValidator that can validate a field (an object of some kind) as it is passed into a method and also be used to validate an instance of that object outside of that context while still using annotations? I came up with the following approach. Let me know what you think of it. Use case:
...
#POST
public Response retrieveSomething(#Encoded #IsAParamValid MyParamObject myParamObject)
{
...
}
I also want to be able to do this somewhere else in code:
IsAParamValidValidator.validate(myParamObject);
without duplicating the logic to do the validation. Critical here is I want the automatic exception-generating service that using #IsAParamValid in a method call gets me.
My idea of a way to get the best of both worlds:
My annotation class:
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Retention(value = RetentionPolicy.RUNTIME)
#Constraint(validatedBy = {IsAParamValidValidator.class})
#Documented
public #interface IsAParamValid
{
String message() default "The param was invalid";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
My validator class:
public class IsAParamValidValidator implements ConstraintValidator<IsAParamValid, MyParamObject>
{
public static void validate(#IsAParamValid MyParamObject myParamObject) {
}
#Override
public void initialize(IsAParamValid isAParamValid) {
}
#Override
public boolean isValid(MyParamObject myParamObject, ConstraintValidatorContext constraintValidatorContext)
{
// do the check
boolean bResult = ... whatever ...
return bResult;
}
}
OK so what do you all think?

Validation with Spring 3.2.0

I'm using HibernateValidator 4.3.1. Validations are performed as intended throughout the entire application.
I have registered some custom editors to perform validation globally such as for ensuring numeric values (double, int etc) in a text-field, for ensuring valid dates regarding the Joda-Time API etc.
In this type of validation, I'm allowing null/empty values by setting the allowEmpty parameter to false as usual to validate it separately especially for displaying separate user friendly error messages when such fields are left blank.
Therefore, in addition to validating with HibernateValidator and custom editors, I'm trying to use the following validation strategy. Again, this kind of validation is only for those fields which are registered for custom editors are when left blank.
The following is the class that implements the org.springframework.validation.Validator interface.
package test;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import validatorbeans.TempBean;
#Component
public final class TempValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
System.out.println("supports() invoked.");
return TempBean.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
TempBean tempBean = (TempBean) target;
System.out.println("startDate = " + tempBean.getStartDate() + " validate() invoked.");
System.out.println("doubleValue = " + tempBean.getDoubleValue() + " validate() invoked.");
System.out.println("stringValue = " + tempBean.getStringValue() + " validate() invoked.");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "startDate", "java.util.date.nullOrEmpty.error");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "doubleValue", "java.lang.double.nullOrEmpty.error");
}
}
The class is designated with the #Component annotation so that it can be auto-wired to a specific Spring controller class. The debugging statements display exactly based on the input provided by a user.
The following is the controller class.
package controller;
import customizeValidation.CustomizeValidation;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.groups.Default;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import test.TempValidator;
import validatorbeans.TempBean;
#Controller
public final class TempController {
#Autowired
private TempService tempService;
private TempValidator tempValidator;
public TempValidator getTempValidator() {
return tempValidator;
}
#Autowired
public void setTempValidator(TempValidator tempValidator) {
this.tempValidator = tempValidator;
}
#RequestMapping(method = {RequestMethod.GET}, value = {"admin_side/Temp"})
public String showForm(#ModelAttribute("tempBean") #Valid TempBean tempBean, BindingResult error, Map model, HttpServletRequest request, HttpServletResponse response) {
return "admin_side/Temp";
}
#RequestMapping(method = {RequestMethod.POST}, value = {"admin_side/Temp"})
public String onSubmit(#ModelAttribute("tempBean") #Valid TempBean tempBean, BindingResult errors, Map model, HttpServletRequest request, HttpServletResponse response) {
//tempValidator.supports(TempBean.class);
//tempValidator.validate(tempBean, errors);
DataBinder dataBinder = new DataBinder(tempBean);
dataBinder.setValidator(tempValidator);
dataBinder.validate();
//errors=dataBinder.getBindingResult();
if (CustomizeValidation.isValid(errors, tempBean, TempBean.ValidationGroup.class, Default.class) && !errors.hasErrors()) {
System.out.println("Validated");
}
return "admin_side/Temp";
}
}
I'm invoking the validator from the Spring controller class itself (which I indeed want) by
DataBinder dataBinder = new DataBinder(tempBean);
dataBinder.setValidator(tempValidator);
dataBinder.validate();
The validator is called but the validation which is expected is not performed.
If only I invoke the validator manually using the following statement (which is commented out above),
tempValidator.validate(tempBean, errors);
then validation is performed. So I don't believe my validator is correctly working. Why does it fail to work with DataBinder?
In my application-context.xml file, this bean is simply configured as follows.
<bean id="tempValidator" class="test.TempValidator"/>
This many packages as below including the test package which the TempValidator class is enclosed within are auto-detected.
<context:component-scan base-package="controller spring.databinder validatorbeans validatorcommands test" use-default-filters="false">
<context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
<context:include-filter expression="org.springframework.web.bind.annotation.ControllerAdvice" type="annotation"/>
</context:component-scan>
I have even tried to put
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
In my dispatcher-servlet.xml file.
What am I overlooking here?
If I understand well what you try to achieve - distinguish between blank fields and incorrect values entered - you can use MUCH MORE SIMPLER approach:
public class MyBean {
#NotNull
#DateTimeFormat(pattern="dd.MM.yyyy HH:mm")
private DateTime date;
#NotNull
#Max(value=5)
private Integer max;
#NotNull
#Size(max=20)
private String name;
// getters, setters ...
}
Controller mapping:
public void submitForm(#ModelAttribute #Valid MyBean myBean, BindingResult result) {
if (result.hasErrors){
// do something}
else{
// do something else
}
}
Validation messages:
NotNull=Required field.
NotNull.date=Date is required field.
NotNull.max=Max is required field.
Size=Must be between {2} and {1} letters.
Max=Must be lower than {1}.
typeMismatch.java.lang.Integer=Must be number.
typeMismatch.org.joda.time.DateTime=Required format dd.mm.yyyy HH:mm
Spring configuration:
#Configuration
public class BaseValidatorConfig {
#Bean
public LocalValidatorFactoryBean getValidator() {
LocalValidatorFactoryBean lvfb = new LocalValidatorFactoryBean();
lvfb.setValidationMessageSource(getValidationMessageSource());
return lvfb;
}
protected MessageSource getValidationMessageSource() {// return you validation messages ...}
}
I can provide more details and explanation, if needed.
I don't know why the approach as mentioned in the question didn't work. I didn't make it work but walking through this document, I found another approach that worked for me as per my requirements.
I set the validator inside a method which was designated by the #InitBinder annotation.
From docs
The Validator instance invoked when a #Valid method argument is
encountered may be configured in two ways. First, you may call
binder.setValidator(Validator) within a #Controller's #InitBinder
callback. This allows you to configure a Validator instance per
#Controller class:
Specifically, in my requirements, the validation should only be performed while updating or inserting data into the database i.e when an associated submit button for those operations is pressed (there is a common button for both of these tasks (insert and update) in my application whose name is btnSubmit).
The validation should be muted in any other case (for example, when the delete button is pressed). To meet this requirement, I have registered the validator as follows.
#InitBinder
protected void initBinder(WebDataBinder binder, WebRequest webRequest) {
if (webRequest.getParameter("btnSubmit") != null) {
binder.setValidator(new TempValidator());
} else {
binder.setValidator(null);
}
}
In this situation, the validator - TempValidator would only be set when the submit button whose name attribute is btnSubmit is clicked by the client.
There is no need for xml configuration anywhere as well as auto-wiring.
The exemplary controller class now looks like the following.
#Controller
public final class TempController {
#Autowired
private TempService tempService;
#InitBinder
protected void initBinder(WebDataBinder binder, WebRequest webRequest) {
if (webRequest.getParameter("btnSubmit") != null) {
binder.setValidator(new TempValidator());
} else {
binder.setValidator(null);
}
}
//Removed the #Valid annotation before TempBean, since validation is unnecessary on page load.
#RequestMapping(method = {RequestMethod.GET}, value = {"admin_side/Temp"})
public String showForm(#ModelAttribute("tempBean") TempBean tempBean, BindingResult error, Map model, HttpServletRequest request, HttpServletResponse response) {
return "admin_side/Temp";
}
#RequestMapping(method = {RequestMethod.POST}, value = {"admin_side/Temp"})
public String onSubmit(#ModelAttribute("tempBean") #Valid TempBean tempBean, BindingResult errors, Map model, HttpServletRequest request, HttpServletResponse response) {
if (CustomizeValidation.isValid(errors, tempBean, TempBean.ValidationGroup.class, Default.class) && !errors.hasErrors()) {
System.out.println("Validated");
}
return "admin_side/Temp";
}
}
The WebRequest paramenter in the initBinder() method is not meant for handling the entire Http request as obvious. It's just for using general purpose request metadata.
Javadocs about WebRequest.
Generic interface for a web request. Mainly intended for generic web
request interceptors, giving them access to general request metadata,
not for actual handling of the request.
If there is something wrong that I might be following, then kindly clarify it or add another answer.

Resources