【问题标题】:Validation with Spring 3.2.0使用 Spring 3.2.0 进行验证
【发布时间】:2013-02-20 01:51:21
【问题描述】:

我正在使用HibernateValidator 4.3.1。在整个应用程序中按预期执行验证。

我已经注册了一些自定义编辑器来执行全局验证,例如确保文本字段中的数值(doubleint 等),确保有关 Joda-Time API 的有效日期等。

在这种类型的验证中,我允许空值/空值,方法是像往常一样将 allowEmpty 参数设置为 false 以单独验证它,特别是当这些字段留空时显示单独的用户友好错误消息。

因此,除了使用 HibernateValidator 和自定义编辑器进行验证之外,我还尝试使用以下验证策略。同样,这种验证仅适用于为自定义编辑器注册的字段留空

以下是实现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");
    }
}

该类使用@Component 注释指定,​​以便它可以自动连接到特定的 Spring 控制器类。调试语句完全根据用户提供的输入显示。

以下是控制器类。

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";
    }
}

我正在通过

从 Spring 控制器类本身(我确实想要)调用验证器
DataBinder dataBinder = new DataBinder(tempBean);
dataBinder.setValidator(tempValidator);
dataBinder.validate();

验证器被调用,但预期的验证执行。

如果我只使用以下语句(上面已注释掉)手动调用验证器,

tempValidator.validate(tempBean, errors);

然后执行验证。所以我不相信我的验证器工作正常。为什么它无法与DataBinder 一起使用?

在我的application-context.xml文件中,这个bean简单配置如下。

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

自动检测到以下许多包,包括 TempValidator 类包含在其中的 test 包。

<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>

我什至试过把

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

在我的dispatcher-servlet.xml 文件中。

我在这里俯瞰什么?

【问题讨论】:

  • 如果你在“dataBinder.validate();”这一行打了个断点走进去,你能看到最终调用的是哪个验证器吗?
  • @CodeChimp - 调用的验证器是问题中指定的验证器 -tempValidator

标签: spring spring-mvc bean-validation spring-3 hibernate-validator


【解决方案1】:

如果我很好地理解您尝试实现的目标 - 区分空白字段和​​输入的错误值 - 您可以使用更简单的方法:

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 ... 
} 

控制器映射:

public void submitForm(@ModelAttribute @Valid MyBean myBean, BindingResult result) {

    if (result.hasErrors){
       // do something}
    else{
       // do something else
    }        
}

验证消息:

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

弹簧配置:

@Configuration
public class BaseValidatorConfig {

    @Bean
    public LocalValidatorFactoryBean getValidator() {

        LocalValidatorFactoryBean lvfb = new LocalValidatorFactoryBean();
        lvfb.setValidationMessageSource(getValidationMessageSource());
        return lvfb;
    }

    protected MessageSource getValidationMessageSource() {// return you validation messages ...}
}

如果需要,我可以提供更多细节和解释。

【讨论】:

  • 啊!我从来没有用过这个LocalValidatorFactoryBean@NotEmpty@NotBlank@NotNull 等注解可以自行工作(在我目前使用的 Spring 3.2.2 中)。这是什么LocalValidatorFactoryBean?而且,@Configuration 比 XML 配置好?谢谢。
  • LocalValidatorFactoryBean 提供了 Spring MVC Validator 和 JSR-303 Bean Validation 实现之间的桥梁 - 例如,它使您能够在控制器方法中使用 @Valid 注释,提供开箱即用的验证错误绑定到示例代码中所示的绑定结果)并且还以 Spring 的方式使用验证消息(错误代码消息解析器)。 @Configuration 提供更大的灵活性并且不易出错,但您可以在 java 和 XML 配置中实现相同的目标。
【解决方案2】:

我不知道为什么问题中提到的方法不起作用。我没有让它工作,但是通过这个document,我找到了另一种根据我的要求对我有用的方法。

我在@InitBinder 注解指定的方法中设置了验证器。

来自docs

@Valid 方法参数被调用时调用的 Validator 实例 遇到的可能配置有两种方式。首先,您可以致电 @Controller 的 @InitBinder 中的 binder.setValidator(Validator) 打回来。这允许您为每个配置一个 Validator 实例 @Controller 类:

具体来说,在我的要求中,仅应在更新或将数据插入数据库时​​执行验证,即当按下这些操作的关联提交按钮时(这两个任务都有一个通用按钮(插入和更新)在我的应用程序中,其名称为 btnSubmit)。

在任何其他情况下(例如,按下删除按钮时),验证应该被静音。为了满足这个要求,我注册了验证器如下。

@InitBinder
protected void initBinder(WebDataBinder binder, WebRequest webRequest) {
    if (webRequest.getParameter("btnSubmit") != null) {
        binder.setValidator(new TempValidator());
    } else {
        binder.setValidator(null);
    }
}

在这种情况下,验证器 - TempValidator 只会在客户端单击名称属性为 btnSubmit 的提交按钮时设置。

在任何地方都不需要xml配置以及自动布线。

示例控制器类现在如下所示。

@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";
    }
}

initBinder() 方法中的WebRequest 参数显然不是用来处理整个 Http 请求的。它只是用于使用通用请求元数据。

关于WebRequest的Javadocs。

Web 请求的通用接口。主要用于通用网络 请求拦截器,使他们能够访问一般请求元数据, 不用于实际处理请求。

如果我可能有什么错误,请澄清或添加另一个答案。

【讨论】:

    猜你喜欢
    • 2019-10-16
    • 1970-01-01
    • 2012-11-27
    • 2013-12-08
    • 2012-08-16
    • 2021-03-30
    • 1970-01-01
    • 2014-07-03
    相关资源
    最近更新 更多