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.