验证器

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

一. 验证概览

  Converter和Formatter作用于field级。在MVC应用 程序中,它们将String转换或格式化成另一种Java类 型,如java.util.Date。验证器则作用于object级。它决定 某一个对象中的所有field是否均是有效的,以及是否遵 循某些规则。

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

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

  

二. Spring验证器

  从一开始,Spring就设计了输入验证,甚至早于 JSR 303(Java验证规范)。因此,Spring的Validation 框架至今都很普遍,尽管对于新项目,一般也建议使用 JSR 303验证器。

  为了创建Spring验证器,要实现 org.springframework.validation.Validator接口。其中有supports和validate两个方法。

 Spring的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表示与被验证对象中的某个 属性相关的一个错误。例如,如果产品的price属性必须 为负数,并且Product对象被验证为负数,那么就需要 创建一个FieldError。例如,在欧洲出售的一本Book, 却在美国的网店上购买,那么就会出现一个 ObjectError。

  编写验证器时,不需要直接创建Error对象,因为 实例化ObjectError或FieldError会花费大量的编程精力。 这是由于ObjectError类的构造器需要4个参数, FieldError类的构造器则需要7个参数,如以下构造器签 名所示:

ObjectError(String objectName, String[] codes, Object[] argumen
ts,
String defaultMessage)
FieldError(String objectName, String field, Object rejectedValue
,
boolean bindingFailure, String[] codes, Object[] argume
nts,
String defaultMessage)

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

  下面是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页面中。错误消息可以通过 Spring支持的国际化特性进行本地化。

三. ValidationUtils类

  org.springframework.validation.ValidationUtils类是 一个工具,有助于编写Spring验证器。不需要像下面这 样:

if (firstName == null || firstName.isEmpty()) {
errors.rejectValue("price");
}

  而是可以利用ValidationUtils类的rejectIfEmpty方 法,像下面这样:

ValidationUtils.rejectIfEmpty("price");

  或者下面的代码:

if (firstName == null || firstName.trim().isEmpty()) {
errors.rejectValue("price");
}

可以编写成:

ValidationUtils.rejectIfEmptyOrWhitespace("price");

public static void rejectIfEmpty(Errors errors, String field,
String errorCode)
public static void rejectIfEmpty(Errors errors, String field,
String errorCode, Object[] errorArgs)
public static void rejectIfEmpty(Errors errors, String field,
String errorCode, Object[] errorArgs, String defaultMes
sage)
public static void rejectIfEmpty(Errors errors, String field,
String errorCode, String defaultMessage)
public static void rejectIfEmptyOrWhitespace(Errors errors,
String field, String errorCode)
public static void rejectIfEmptyOrWhitespace(Errors errors,
String field, String errorCode, Object[] errorArgs)
public static void rejectIfEmptyOrWhitespace(Errors errors,
String field, String errorCode, Object[] errorArgs,
String defaultMessage)
public static void rejectIfEmptyOrWhitespace(Errors errors,
String field, String errorCode, String defaultMessage)

  此外,ValidationUtils还有一个invokeValidator方 法,用来调用验证器

public static void invokeValidator(Validator validator,
Object obj, Errors errors)

四. Spring的Validator范例

  app21a应用程序中包含一个名为ProductValidator的 验证器,用于验证Product对象。

app21a的Product

package app21a.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;
//此处没有显示getters和setters方法
}

 

ProductValidator类

package validator;

import java.util.Date;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import domain.Product;

public class ProductValidator implements Validator {
    /*
     * 如果验证器可以处理指定的Class,supports方法将返回true。 validate方法会验证目标对象,并将验证错误填入Errors对象。
     */
    @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");
            }
        }
    }
}

    ProductValidator验证器是一个非常简单的验证器。 它的validate方法会检验Product是否有名称和价格,并 且价格是否不为负数。它还会确保生产日期不晚于今天 的日期 

五. 源文件

  验证器不需要显式注册,但是如果想要从某个属性 文件中获取错误消息,则需要通过声明 messageSource bean,告诉 Spring 要去哪里查找这个文件。下面是 app21a 中的messageSource bean:

      <context:component-scan base-package="controller"></context:component-scan>
        
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <!-- 前缀 和 后缀 -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
        </bean>
        <!-- 告诉 Spring 要去哪里查找这个错误文件 -->
    <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="/WEB-INF/resource/messages"/>
</bean>

  这个bean本质上是说,错误码和错误消息可以 在/WEB-INF/resource目录下的messages. properties文件 中找到。

messages.properties文件的内容

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

6. Controller类

  在Controller类中通过实例化validator类,可以使用 Spring验证器。

  saveProduct方法创建了一个ProductValidator,并调用其 validate方法。为了检验该验证器是否生成错误消息, 须在BindingResult中调用hasErrors方法。

ProductController类

package 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 domain.Product;
import 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) {
        
        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";
        }
        
        return "ProductDetails";
    }
}

  使用Spring验证器的另一种方法是:在Controller中 编写initBinder方法,并将验证器传到WebDataBinder, 并调用其validate方法:

@org.springframework.web.bind.annotation.InitBinder
public void initBinder(WebDataBinder binder) {
// this will apply the validator to all request-handling me
thods
binder.setValidator(new ProductValidator());
binder.validate();
}

  将验证器传到WebDataBinder,会使该验证器应用 于Controller类中所有处理请求的方法。

  或者利用@javax.validation.Valid对要验证的对象参 数进行注解。例如:

public String saveProduct(@ModelAttribute Product product,
BindingResult bindingResult, Model model) {

  

八.JSR 303验证

   JSR 303“Bean Validation”(发布于2009年11月)和 JSR 349“Bean Validation 1.1”(发布于2013年5月)指定 了一整套API,通过注解给对象属性添加约束。JSR 303 和JSR 349可以分别从以下网址下载:

http://jcp.org/en/jsr/detail?id=303
http://jcp.org/en/jsr/detail?id=349

 

   当然,JSR只是一个规范文档,本身用处不大,除 非编写了它的实现。对于JSR bean validation,目前有两 个实现。第一个实现是Hibernate Validator,目前版本为 5,JSR 303和JSR 349两种它都实现了,可从以下网站 下载:

http://sourceforge.net/projects/hibernate/files/hibernate-validator/

  第二个实现是Apache BVal,它只实现了JSR 303, 可从以下网站下载:

http://bval.apache.org/downloads.html

  注意,下面这个网站对于与Java bean validation相 关的一切内容都是很重要的:

  JSR 303不需要编写验证器,但要利用JSR 303标注 类型嵌入约束。

JSR 303约束
属性描述范例
@AssertFalse应用于boolean属性,该属性值必须为False@AssertFalsebooleanhasChildren;
@AssertTrue应用于boolean属性,该属性值必须为True@AssertTruebooleanisEmpty;
@DecimalMax该属性值必须为小于或等于指定值的小数@DecimalMax("1.1") BigDecimal price;
@DecimalMin该属性值必须为大于或等于指定值的小数@DecimalMin("0.04") BigDecimal   price;
@Digits该属性值必须在指定范围内。integer属性定义该数值的最大整数部分,fraction属性定义该数值的最大小数部分@Digits(integer=5,fraction=2)  BigDecimal price;
@Future该属性值必须是未来的一个日期@FutureDateshippingDate;
@Max该属性值必须是一个小于或等于指定值的整数@Max(150)int age;
@Min该属性值必须是一个大于或等于指定值的整数@Max(150)int age;
@NotNull该属性值不能为Null@NotNullString firstName;
@Null该属性值必须为Null@NullString testString;
@Past该属性值必须是过去的一个日期@PastDate birthDate;
@Pattern该属性值必须与指定的常规表达式相匹配@Pattern(regext="\d{3}")String areaCode;
@Size该属性值必须在指定范围内Size(min=2,max=140)String   description;

 

  一旦了解了JSR 303 validation的使用方法,使用起 来会比Spring验证器还要容易些。像使用Spring验证器 一样,可以在属性文件中以下列格式使用property键, 来覆盖来自JSR 303验证器的错误消息:

constraint.object.property

  例如,为了覆盖以@Size注解约束的Product对象的 name属性,可以在属性文件中使用下面这个键:

Size.product.name

  为了覆盖以@Past注解约束的Product对象的 productionDate属性,可以在属性文件中使用下面这个 键:

Past.product.productionDate

九. JSR 303 Validator范例

 app21b应用程序展示了JSR 303输入验证的例子。 这个应用程序是对app21a进行修改之后的版本,与之前 的版本有一些区别。首先,它没有 ProductValidator 类。其次,来自 Hibernate Validator库的JAR文件已经被 添加到WEB-INF/lib中。

   

  

  
    

  

 

   

转载于:https://www.cnblogs.com/jiangfeilong/p/10808939.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值