Validation with Spring 3.2.0

I am using HibernateValidator 4.3.1 . Checks are performed as intended throughout the application.

I have registered some custom editors to do validation around the world, for example, to provide numeric values ​​( double

, int

etc.) in a text field, to provide correct dates with respect to the Joda-Time API, etc.

In this type of validation, I allow null / empty values ​​by setting the parameter allowEmpty

to false

, as usual, to validate it separately, especially to display separate handy error messages when such fields are left empty.

Therefore, in addition to validation with HibernateValidator and custom editors, I am trying to use the following validation strategy. Again, this check is performed only for those fields that are registered with custom editors when left blank .

Below is the class that implements the interface org.springframework.validation.Validator

.

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 annotated @Component

, so it can be automatically connected to a specific Spring controller class. Debug statements are displayed accurately based on the input provided by the user.

Below 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 am calling the validator from the Spring controller class itself (which I really want) on

DataBinder dataBinder = new DataBinder(tempBean);
dataBinder.setValidator(tempValidator);
dataBinder.validate();

      

The validator is called, but the expected validation fails .

If only I call the validator manually using the following statement (which is commented above),

tempValidator.validate(tempBean, errors);

      

then the check is performed. So I don't believe my validator is working correctly. Why isn't he working with DataBinder

?

In my application-context.xml

file, this bean is just configured like this.

<bean id="tempValidator" class="test.TempValidator"/>

      

There are many packages as shown below, including the package test

that encloses the class TempValidator

.

<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 even tried to put

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

      

In my dispatcher-servlet.xml

file.

What can't I see here?

+3


source to share


2 answers


If I understand well what you are trying to achieve - distinguish between empty fields and invalid values ​​entered - you can use MUCH MORE SIMPLER:

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.

+4


source


I don't know why the approach mentioned in the question didn't work. I didn't work, but going through this document I found a different approach that worked for me as per my requirements.

I set up a validator inside the method, which was annotated @InitBinder

.

From docs

The Validator instance is called when the @Valid method argument can be configured in two ways. First, you can call binder.setValidator (Validator) at @Controller @InitBinder Call Back. This allows you to customize your Validator instance per @Controller class:

In particular, in my requirements, validation should only be done when updating or inserting data into the database, i.e. when the associated submit button for these operations is clicked (there is a common button for both of these tasks (insert and update) in my application, whose name is btnSubmit

).

Validation should be disabled in any other case (for example, when clicking the delete button). To fulfill this requirement, I 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

will be set only if the submit button whose name attribute is btnSubmit

clicked by the client.



No need to set up xml anywhere and also with automatic posting.

An example controller class now looks like this.

@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 parameter WebRequest

in the method is initBinder()

not intended to treat the entire Http request as obvious. It's just for general purpose query metadata use.

Javadocs about WebRequest

.

Common interface for web request. Mainly intended for the public network to query interceptors, giving them access to the metadata of the general query rather than actually handling the request.

If there is something wrong that I can follow then kindly clarify it or add another answer.

0


source







All Articles