输入校验是Web开发最重要的任务之一。SpringMVC作为当今最流行的MVC框架,自然也对输入校验做了很好的支持。
在SpringMVC中可以用两种方式来进行输入校验
1)利用SpringMVC自带的校验框架
2)利用JSR 303
下面我们对着两种校验方式做详细的描述。
Validator
接口,该接口的签名是这样的:public interface Validator {
boolean supports(Class<?> clazz);
void validate(Object target, Errors errors);
}
Spring在完成校验动作时,首先会调用supoorts()方法,判断当前的object在不在校验队列中,如果在,则紧接着调用validate方法,进行校验。
在应用校验框架是,我们最常用的可能就是Errors接口了,这个接口有一些实现类,我们可以吧它理解为一个存放错误信息的容器,里面存放着一系列的域错误(FieldError)和对象错误(ObjectError),同时,它还提供了获取错误信息和加入错误信息的方法,例如:
void reject(String errorCode, Object[] errorArgs, String defaultMessage);
void rejectValue(String field, String errorCode, Object[] errorArgs, String defaultMessage);
boolean hasFieldErrors();
List<FieldError> getFieldErrors();
List<ObjectError> getAllErrors();
......
利用这些方法,我们就可以在这个“错误容器”里方便的存取错误数据。
在进行数据校验时,我们以往经常要写很多向下面这样 代码:
if(firstName == null && firstName.isEmpty())
errors.rejectValue("price");
或者
if(firstName == null || firstName.trim().isEmpty())
errors.rejectValue("price");
这样的代码虽然是必须的,但是大量的重复往往令人头疼,幸好,Spring为我们提供了一个工具类,可以很好的解决上面的问题,ValidtionUtils
,利用这个工具类就可以吧具有上面功能 代码写的很优雅:
ValidationUtils.rejectIfEmpty("price");
ValidationUtils.rejectIfEmptyOrWhitespace("price");
该工具类还保护很多重载方法,大家可以自己查看API文档。
看起来也是蛮强大的吧……
下面,我们就用一个简单是范例,来说明一下Spring校验框架的基本用法。
1)首先,创建一个POJO类
@SuppressWarnings("serial")
public class Product implements Serializable{
private String name;
private String description;
private Float price;
private Date productionDate;
setter/getter....
}
2)编写一个实现了Validator接口的校验器
public class ProductValidator implements Validator{
Logger logger = Logger.getLogger(ProductValidator.class);
//spring通过调用该方法确定是否对某个对象进行校验(validate(target,errors))
@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( //验证null 和 "" 空字符串
errors,
"name", //域名称
"productname.required" //错误编码
);
ValidationUtils.rejectIfEmpty(
errors,
"price",
"price.required"
);
ValidationUtils.rejectIfEmpty(errors,
"productionDate", "productionDate.required");
//手动向errors中添加错信息的示例
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())){
logger.info(" productDate error");
errors.rejectValue("productionDate", "productiondate.invalid");
}
}
}
}
3)创建一个用于显示错误信息的资源文件(.properties)
productname.required.product.name=Please enter a product name
price.required=Please enter a price
productiondate.required=Please enter a production date
productiondate.invalid=Invalid production date. Please ensure the production date is not later than today
4)紧接着,要把这个资源文件注册到spring环境中,这样在检验器中才能得到具体的错误提示。
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="/WEB-INF/resource/messages"></property>
</bean>
5)最后,要编写一个应用了校验器的Controller来完成校验任务。
/** * @Description: 校验框架测试 controller. * @Author:Zander * @CreateDate:Aug 8, 2015 * @Company: */
@Controller
public class ProductController {
private static final Logger logger = Logger.getLogger(ProductController.class);
@RequestMapping(value="/product_input")
public String inutProduct(Model model){
model.addAttribute("product", new Product());
return "productionForm";
}
public String saveProduct(@ModelAttribute Product product,
BindingResult bindingResult, Model model){
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";
}
//校验通过
model.addAttribute("product", product);
return "productDetails";
}
}
完成。
下面先说明一下JSR 303的规范。
@AssertFalse 应用于boolean属性,该属性值必须为False
@AssertFalse boolean hasChildern
@AssertTrue 应用于boolean属性,该属性值必须为True
@AssertTrue boolean isEmpty
@DecimalMax 该属性必须为小于或等于指定的小树 @DecimalMax("1.1") BigDecimal price
@DecimalMin 该属性必须大于或等于指定值的小数 @DecimalMin("0.04")
@Digits 该属性必须在指定的范围内
@Digits(integer=5,fraction=2)
BigDecimal price;
@Future 该属性值必须是未来的一个日期 @Future Date shippingDate
@Max 该属性值必须是一个小于或等于指定数值的整数 @Max(150) int age
@Min 该属性值必须是一个大于或等于指定数值的整数
@Min(150) int age
@NotNull String firstName
@Null 该值必须为null
@Null String testString
@Past 该值必须为过去的一个时间 @Past Date birthDate
@Pattern 该属性hi必须与指定的常规表达式匹配 @Pattern(regext="\\d{3}")
@Size 该属性值必须在制定范围内 @Size(min=2,max=140) String description
利用这些注解直接表述bean中的域就可以方便的实现校验。
下面我们还是通过一个小例子,来看一下它的具体用法。
1)创建JavaBean,利用JSR注解标注
/** * @Description: JSR校验 * @Author:Zander * @CreateDate:Aug 8, 2015 * @Company: */
public class Product2 {
@Size(min=1,max=10)
private String name;
private String description;
private Float price;
@Past
private Date productionDate;
geter()/setter()
...
}
2)创建Controller,不用实现校验器了哦!这是重点。
/** * @Description: JSR 校验测试Controller. * @Author:Zander * @CreateDate:Aug 8, 2015 * @Company: */
@Controller
public class ProductController2 {
private static final Logger logger = Logger.getLogger(ProductController2.class);
public String inutProduct(Model model){
model.addAttribute("product", new Product2());
return "productForm";
}
public String saveProduct(@Valid @ModelAttribute Product2 product2,
BindingResult bingBindingResult, Model model){
//判断是否通过校验 此处利用@Valid 注解 加上 JSR注解 就不用显示的创建校验器
if(bingBindingResult.hasErrors()){
FieldError fieldError = bingBindingResult.getFieldError();
logger.info("Code:" + fieldError.getCode() + ", object:" + fieldError.getObjectName() +
", field : " + fieldError.getField());
return "productForm";
}
//save product
model.addAttribute("product", product2);
return "productDetails";
}
}
3)配置一个资源文件,用于提示信息的显示。
Size.product.name=Product name mast be 1 to 10 characters long
Past.product.productionDate=Production date must a past time
4)再像上面的那样,注册资源文件到spring容器。
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="/WEB-INF/resource/messages"></property>
</bean>
哦了,搞定,就这么用。