在Spring MVC中,有两种方式可以验证输入,即利用Spring自带的验证框架,或者利用JSR 303实现。
1.验证概览
Converter和Formatter作用于field级。在MVC应用程序中,它们将String转换或格式化成另一种Java类型。验证器则作用于object级。它决定某一个对象中的所有field是否均是有效的,以及是否遵循某些规则。
如果一个应用程序中即使用了Formatter,又有validator(验证器),那么,它们的事件顺序是这样的:在调用Controller期间,将会有一个或者多个Formatter,试图将输入字符串转换成domain对象中的field值。一旦格式化成功,验证器就会介入。
2.Spring验证器
为了创建Spring验证器,要实现org.springframework.validation.Validator接口。
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表示与被验证对象中的某个属性相关的一个错误。
编写验证器时,不需要直接创建Error对象,因为实例化ObjectError或FieldError花费了大量的编程精力。给Errors对象添加错误的最容易的方法是:在Errors对象上调用一个reject或者rejectValue方法。
void reject(String errorCode)
void reject(String errorCode, String defaultMessage)
void rejectValue(String field, String errorCode)
void rejectValue(String field, String errorCode, String defaultMessage)
大多数时候,只给reject或者rejectValue方法传入一个错误码,Spring就会在属性文件中查找错误码,获得相应的错误消息。还可以传入一个默认消息,当没有找到指定的错误码时,就会使用默认消息。
Errors对象中的错误消息,可以利用表单标签库的Errors标签显示在HTML页面中。
3.ValidationUtils类
org.springframework.validation.ValidationUtils类是一个工具,有助于编写Spirng验证器。
4.Spring的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<?> clazz)
{
return Product.class.isAssignableFrom(clazz);
}
@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)
{
if (productionDate.after(new Date()))
{
errors.rejectValue("productionDate", "productiondate.invalid");
}
}
}
}
验证器不需要显式注册,但是如果想要从某个属性文件中获取错误信息,则需要通过声明messageSource bean,告诉Spring要去哪里查找这个文件。
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="/WEB-INF/resource/messages" />
</bean>
messages.properties
productname.required=Please enter a product name
price.required=Please enter a price
productiondate.required=Please enter a production date
price.negative=Invalid price.Please enter a price greater than zero
productiondate.invalid=Invalid production date.Please ensure the production date is not later than today
使用Spring验证器的另一种方法是:在Controller中编写initBinder方法,并将验证器传到WebDataBinder,并调用其validate方法。
@org.springframework.web.bind.annotation.InitBinder
public void InitBinder(WebDataBinder binder)
{
binder.setValidator(new ProductValidator());
binder.validate();
}
8.JSR 303验证
对于JSR bean validation,目前有两个实现。第一个实现时Hibernate Validator,第二个实现时Apache BVal。
JSR 303不需要编写验证器,但要利用JSR 303标注类型嵌入约束。
属性 | 描述 |
---|---|
@AssertFalse | 应用于boolean属性,该属性值必须为False |
@AssertTrue | 应用于boolean属性,该属性值必须为True |
@DecimalMax | 该属性值必须为小于或等于指定值的小数 |
@DecimalMin | 该属性值必须为大于或等于指定值的小数 |
@Digits | 该属性值必须在指定范围内。integer属性定义该数值的最大整数部分,fraction属性定义该数值的最大小数部分 |
@Future | 该属性值必须是一个未来的日期 |
@Max | 该属性值必须是一个小于或等于指定值的整数 |
@Min | 该属性值必须是一个大于或等于指定值的整数 |
@NotNull | 该属性值不能为Null |
@Null | 该属性值必须为Null |
@Past | 该属性值必须是过去的一个日期 |
@Pattern | 该属性值必须与指定的常规表达式相匹配 |
@Size | 该属性值必须在指定范围内 |
9.JSR 303 Validator范例
jar包
<mvc:annotation-driven validator="validator" />
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
<property name="validationMessageSource" ref="messageSource" />
</bean>
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="/WEB-INF/resource/messages" />
</bean>
@RequestMapping(value = "/product_save")
public String saveProduct(@Valid @ModelAttribute Product product, BindingResult bindingResult,
Model model)
package app07b.domain;
import java.io.Serializable;
import java.util.Date;
import javax.validation.constraints.Past;
import javax.validation.constraints.Size;
import com.sun.istack.internal.NotNull;
public class Product implements Serializable
{
private static final long serialVersionUID = -164834188493691288L;
@Size(min = 1, max = 10)
private String name;
@NotNull
private String description;
private Float price;
@Past
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;
}
}
由于JSR 303是正式的Java规范,因此新项目建议使用JSR 303验证器。