SpringMVC开发技术~8_3~验证器Validate

1 验证概述

输入验证是Spring处理的最重要Web开发任务之一。
在Spring MVC中,有两种方式可以验证输入,利用Spring自带的验证框架,或者利用JSR 303实现。

验证器作用于Object级。它决定某一个对象中的所有field是否均是有效的,以及是否遵循某些规则。

如果一个应用程序中既使用了Formatter,又有validator(验证器),那么,调用的顺序是这样的:在调用controller期间,将会有一个或者多个Formatter,试图将输入字符串换成domain对象中的field值,一旦格式化成功,验证器就会介入。

例如,Order对象可能会有一个shippingDate属性(其类型显然为Date),它的值绝对不可能早于今天的日期。当调用OrderController时,DateFormatter会将字符串转化成Date,并将它赋予Order对象的shippingDate属性。如果转换失败,用户就会被转回到前一个表单。如果转换成功,则会调用转换器,查看shippingDate是否早于今天的日期。

现在,你或许会问,将验证逻辑移到DataFormatter中是否更加明智?因为比较一下日期并非难事,但答案却是否定的。
首先,DataFormatter还可以将其他字符串格式化成日期,如birthDate或者purchaseDate,这两个日期的规则都不同于shippingDate。事实上,比如,员工的出生日期绝对不可能睌于今日。
其次,验证器可以检验两个或更多字段之间的关系,各个字段均受不同Formatter的支持。例如,假设Employee对象有BirthDate属性和startDate,验证器就可以设定规则,使任何员工的入职日期均不可能早于他或她的出生日期。因此有效的Employee对象必须让它的birthDate属性值早于其startDate值。这就是验证器的任务。

2 Spring验证器

从一开始,Spring就设计了输入验证,甚至早于JSR 303(Java验证规范)。因此,Spring的Validation框架至今都很普遍,尽管对于新项目,一般建议使用JSR 303验证器。
为了创建Spring验证器,要实现org.sprin gfram ework.validation.Validator接口。这个接口有supports和validate两个方法。

package  org.springframework.validation;
public interface Validator{
	boolean  supports<Class<?>  clazz>
	void  validate(Object  target, Errors  errors);
}

如果验证器可以处理指定的Class,supports方法将返回true。Validate方法会验证目标对象,并将验证错误填入Errors对象。
Errors对象是org.springframework.validation.Errors接口的一个实例。Errors对象中包含了一系列FieldError和ObjectError对象。FieldError表示与被验证对象中的某介属性相关的一个错误。例如,如果产品的price属性必须为负数,并且Product对象被验证为负数,那么就需要创建一个FieldError。例如,在欧洲出售的一本Book,却在美国的网店上购买,那么就会出现一个ObjectError。
编写验证器时,不需要直接创建Error对象,因为实例化ObjectError或FieldError花费了大量的编程精力。这是由于ObjectError类的构造器需要4个参数,FieldError类的构造器则需要7个参数,如以下构造器签名所示:

ObjectError(String  objectName, String[]  codes, Object[]  arguments, String  defaultMessage)
FieldError(String  objectName,String  field, Oject  rejectedValue,  Boolean  bindingFailure, 
String[]  codes, Object[]  arguments, String   defaultMessage)

给Errors对象添加错误的最容易的方法是:在Errors对象上调用一个reject或者蝎ectValue方法。调用reject,往FieldError中添加一个ObjectError和rejectValue。

3 Spring的Validator案例

3.1 案例的程序目录

在这里插入图片描述

3.2 domain

package app07a.domain;
import java.io.Serializable;
import java.util.Date;
public class Product implements Serializable {
    private static final long serialVersionUID = 748392348L;
	private String name;
    private String description;
    private Float price;
    private Date productionDate;
    public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public Float getPrice() {
        return price;
    }
    public void setPrice(Float price) {
        this.price = price;
    }
    public Date getProductionDate() {
        return productionDate;
    }
    public void setProductionDate(Date productionDate) {
        this.productionDate = productionDate;
    }    
}

3.3 controller控制器

package app07a.controller;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import app07a.domain.Product;
import app07a.validator.ProductValidator;
@Controller
public class ProductController {
    private static final Log logger = LogFactory.getLog(ProductController.class);
    @RequestMapping(value = "/product_input")
    public String inputProduct(Model model) {
        model.addAttribute("product", new Product());
        return "ProductForm";
    }
    @RequestMapping(value = "/product_save")
    public String saveProduct(@ModelAttribute Product product,
            BindingResult bindingResult, Model model) {
        logger.info("product_save");
        System.out.println("prod save");
        ProductValidator productValidator = new ProductValidator();
        productValidator.validate(product, bindingResult);

        if (bindingResult.hasErrors()) {
            FieldError fieldError = bindingResult.getFieldError();
            logger.info("Code:" + fieldError.getCode() + ", field:"
                    + fieldError.getField());
            return "ProductForm";
        }
        // save product here
        model.addAttribute("product", product);
        return "ProductDetails";
    }
}

3.4 formatter格式化器

package app07a.formatter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import org.springframework.format.Formatter;
public class DateFormatter implements Formatter<Date> {
    private String datePattern;
    private SimpleDateFormat dateFormat;
    public DateFormatter(String datePattern) {
        this.datePattern = datePattern;
        dateFormat = new SimpleDateFormat(datePattern);
        dateFormat.setLenient(false);
    }
    @Override
    public String print(Date date, Locale locale) {
        return dateFormat.format(date);
    }
    @Override
    public Date parse(String s, Locale locale) throws ParseException {
        try {
            return dateFormat.parse(s);
        } catch (ParseException e) {
            // the error message will be displayed when using <form:errors>
            throw new IllegalArgumentException(
                    "invalid date format. Please use this pattern\""
                            + datePattern + "\"");
        }
    }
}

3.5 validator验证器

package app07a.validator;
import java.util.Date;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import app07a.domain.Product;
public class ProductValidator implements Validator {
    @Override
    public boolean supports(Class<?> klass) {
        return Product.class.isAssignableFrom(klass);
    }
    @Override
    public void validate(Object target, Errors errors) {
        Product product = (Product) target;
        ValidationUtils.rejectIfEmpty(errors, "name", "productname.required");
        ValidationUtils.rejectIfEmpty(errors, "price", "price.required");
        ValidationUtils.rejectIfEmpty(errors, "productionDate", "productiondate.required");        
        Float price = product.getPrice();
        if (price != null && price < 0) {
            errors.rejectValue("price", "price.negative");
        }
        Date productionDate = product.getProductionDate();
        if (productionDate != null) {
            // The hour,minute,second components of productionDate are 0
            if (productionDate.after(new Date())) {
                System.out.println("salah lagi");
                errors.rejectValue("productionDate", "productiondate.invalid");
            }
        }
    }
}

3.6 Spring MVC配置文件

<?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:p="http://www.springframework.org/schema/p"
	xmlns:mvc="http://www.springframework.org/schema/mvc" 
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd     
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
	<context:component-scan base-package="app07a.controller" />
    <context:component-scan base-package="app07a.formatter" />
	<mvc:annotation-driven conversion-service="conversionService" />
	<mvc:resources mapping="/css/**" location="/css/" />
	<mvc:resources mapping="/*.html" location="/" />
	<bean id="viewResolver"
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/jsp/" />
		<property name="suffix" value=".jsp" />
	</bean>	
    <bean id="messageSource" 
            class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="/WEB-INF/resource/messages" />
    </bean>	
    <bean id="conversionService"
        class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="formatters">
            <set>
                <bean class="app07a.formatter.DateFormatter">
                    <constructor-arg type="java.lang.String" value="MM-dd-yyyy" />
                </bean>
            </set>
        </property>
    </bean>
</beans>

3.7 View视图层

3.7.1 ProductForm.jsp

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML>
<html>
<head>
<title>Add Product Form</title>
<style type="text/css">@import url("<c:url value="/css/main.css"/>");</style>
</head>
<body>
<div id="global">
<form:form commandName="product" action="product_save" method="post">
    <fieldset>
        <legend>Add a product</legend>
        <p class="errorLine">
            <form:errors path="name" cssClass="error"/>
        </p>
        <p>
            <label for="name">*Product Name: </label>
            <form:input id="name" path="name" tabindex="1"/>
        </p>
        <p>
            <label for="description">Description: </label>
            <form:input id="description" path="description" tabindex="2"/>
        </p>
        <p class="errorLine">
            <form:errors path="price" cssClass="error"/>
        </p>
        <p>
            <label for="price">*Price: </label>
            <form:input id="price" path="price" tabindex="3"/>
        </p>
        <p class="errorLine">
            <form:errors path="productionDate" cssClass="error"/>
        </p>
        <p>
            <label for="productionDate">*Production Date: </label>
            <form:input id="productionDate" path="productionDate" tabindex="4"/>
        </p>
        <p id="buttons">
            <input id="reset" type="reset" tabindex="5">
            <input id="submit" type="submit" tabindex="6"  value="Add Product">
        </p>
    </fieldset>
</form:form>
</div>
</body>
</html>

3.7.2 ProductDatails.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML>
<html>
<head>
<title>Save Product</title>
<style type="text/css">@import url("<c:url value="/css/main.css"/>");</style>
</head>
<body>
<div id="global">
    <h4>The product has been saved.</h4>
    <p>
        <h5>Details:</h5>
        Product Name: ${product.name}<br/>
        Description: ${product.description}<br/>
        Price: $${product.price}
    </p>
</div>
</body>
</html>

3.7.3 ProductView.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML>
<html>
<head>
<title>View Product</title>
<style type="text/css">@import url("<c:url value="/css/main.css"/>");</style>
</head>
<body>
<div id="global">
    <h4>${message}</h4>
    <p>
        <h5>Details:</h5>
        Product Name: ${product.name}<br/>
        Description: ${product.description}<br/>
        Price: $${product.price}
    </p>
</div>
</body>
</html>

3.8 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" 
id="WebApp_ID" version="3.1">
  <display-name>YJYSpring_MVC_Validator_PaulDeckCH07</display-name>
  <welcome-file-list>    
    <welcome-file>index.jsp</welcome-file>    
  </welcome-file-list>
  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/config/springmvc-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

4 运行和测试

在浏览器中输入

http://localhost:8080/YJYSpring_MVC_Validator_PaulDeckCH07/product_input

在这里插入图片描述
如果不输入任何信息,就提交显示如下界面
在这里插入图片描述
如果输入部分错误信息
在这里插入图片描述
输入正确信息,显示如下界面,其中日期不能晚于今天
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值