zoukankan      html  css  js  c++  java
  • Struts2入门---输入验证---验证身份证案例

    1. Struts2 的验证

    1). 验证分为两种:

    > 声明式验证*

    >> 对哪个 Action 或者 Model 的那个字段进行验证
    >> 使用什么验证规则
    >> 如果验证失败, 转向哪一个页面, 显示什么错误消息

    > 编程式验证

    2). 声明验证的 helloworld

    I. 先明确对哪一个 Action 的哪一个字段进行验证: (age)
    II. 编写配置文件:
    > 把 struts-2.5.12appsstruts2-showcaseWEB-INF 下的 validation.xml 文件复制到当前 Action所在的包下.(Struts2 2.5版本)
    > 因为学习资料都是2.3 版本的所以这里需要将validation.xml 文件的头部
    <!DOCTYPE form-validation PUBLIC
    "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.3.0//EN"
    "http://jakarta.apache.org/commons/dtds/validator_1_3_0.dtd">
    修改为
    <!DOCTYPE validators PUBLIC
    "-//Apache Struts//XWork Validator 1.0.2//EN"
    "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
    导入对应的 dtd 文件就可以了.(这样与大多数教学视频版本一致, 在配置时也会有对应的提示 , 对于自学者来说轻松许多).
    > 把该配置文件名改为 : ActionClassName-validation.xml .
    > 编写验证规则: 参见 struts-2.5.12/docs/docs/int-validator.html 文档即可.
    > 在配置文件中看定制错误消息:

    <field name="age">
      <field-validator type="int">
        <param name="min">20</param>
        <param name="max">50</param>
        <message>Age needs to be between ${min} and ${max}</message>
      </field-validator>
    </field>

    > 该错误消息可以国际化吗 ? 可以:

    >> 在配置文件中加入 key: <message key="error.int"></message>
    >> 在国际化资源文件中加入键值对 : error.int = Age needs to be between ${min} and ${max}

    III. 若验证失败, 则转向 input 的 那个result, 所以需要配置 name = input 的 result
    <result name="input">/validation.jsp</result>

    IV. 如何显示错误消息呢?

    > 若使用的 非 simple 主题, 则自动显示错误消息.
    > 若使用的是 simple 主题, 则需要 s:fielderror 标签或者 EL 标签来手动进行显示.

    <s:filederror name="age"></s:filederror>
    OR
    ${filedErrors.age[0] }

    3). 注意: 若一个 Action 类可以应答多个action 请求使用不同的验证规则, 怎么办?

    > 为每一个不同的 action 请求定义其对应的验证文件: ActionClassName-AliasName-validation.xml

    > 不带别名的配置文件: ActionClassName-validation.xml 中的验证规则依然会发发生作用. (可以把 action 共用的验证规则配置在里面)

    4). 声明式验证框架的原理:

    > Struts2 默认的拦截器栈中提供了一个 validation 拦截器

    > 每一个具体的验证规则对应具体一个验证器. 参看 com.opensymphony.xwork2.validator.validators 下的 default.xml 发现它把验证器与验证规则相关联
    <validator name="int" class="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"/>

    5). 短路验证: 若对一个字段使用多个验证器, 默认情况下会执行所有的验证. 若希望前面的验证器没有通过, 后面的就不在验证, 看可以使用短路验证.

    <!-- 设置短路验证: 若当前验证没用通过, 则不再进行下面的验证 -->
    <field-validator type="conversion" short-circuit="true">
    <message>Conversion Error Occurred</message>
    </field-validator>
    <field-validator type="int">
    <param name="min">20</param>
    <param name="max">50</param>
    <message key="error.int"></message>
    </field-validator>

    6). 若类型转换失败, 默认情况下 还会执行后面的拦截器, 还会进行验证. 可以通过修改 ConversionErrorInterceptor 源代码的方式(新建同样的包,及文件到达覆盖的效果,切记源文件不要随便修改都是复制出来修改.)
    当类型转换失败时, 不在执行后续的验证拦截器, 而是直接返回 input 的result

    Object action = invocation.getAction();
    if (action instanceof ValidationAware) {
        ValidationAware va = (ValidationAware) action;
        //如果已经有错误发生,就不再向后面执行而是直接返回到input页面.
        if(va.hasFieldErrors() || va.hasActionErrors()){
            return "input";
        }
    }    

    7). 关于非字段验证: 不是针对某一个字段的验证

    <validator type="expression">
    <param name="expression"><![CDATA[password==password2]]></param>
    <message>两次密码不一致!</message>
    </validator>
    显示非字段验证的错误消息, 使用 s:actionerror 标签: <s:actionerror/>

    8). 不同的字段使用同样的验证规则, 而且使用同样的响应消息.

    可以在国际化资源文件中:

    error.int = ${getText(fieldName)} needs to be between ${min} and ${max}

    age=Age
    count=Count

    9). 自定义验证器

    I. 定义一个验证器的类

    > 自定义验证器都需要实现 Validator.
    > 可以选择继承 ValidatorSupport 或 FieldValidatorSupport 类
    > 若希望实现一个一般的验证器, 则可以继承 ValidatorSupport
    > 若希望实现一个字段验证器, 则可以继承 FieldValidatorSupport

    > 具体实现可以参考目前已经有的验证器.

    > 若验证程序需要接受一个输入参数, 需要为这个参数增加一个对应的属性.

    II. 在配置文件中配置验证器

    > 默认情况下, Struts2 会在类路径的根目录下加载 validators.xml 文件 . 在该文件中加载验证器.
    该文件的定义方式同默认的验证器的那个配置文件: com.opensymphony.xwork2.validator.validators 下的 default.xml

    > 若类路径下没有指定的验证器, 则从 com.opensymphony.xwork2.validator.validators 下的 default.xml 中的验证器加载.
    III. 使用 : 和目前的验证器一样

    IV.  示例代码: 自定义一个 身份证验证器

    1). 表单输入页面 input.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib uri="/struts-tags" prefix="s"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
        <s:actionerror/>    
        <s:form action="testValidation">
            <s:textfield name="idCard" label="idCard"></s:textfield>    
            <s:submit></s:submit>
        </s:form>
    </body>
    </html>
    input.jsp

    2). 一个身份证验证类CheckIdCard(网上有许多不需要自己写, 只要能看懂会用就行)

      1 package com.struts.validation.app;
      2 
      3 import java.text.SimpleDateFormat;  
      4 import java.util.Date;  
      5 /** 
      6  * 验证身份证号码 身份证号码, 可以解析身份证号码的各个字段,以及验证身份证号码是否有效; 身份证号码构成:6位地址编码+8位生日+3位顺序码+1位校验码 
      7  *  
      8  */  
      9 public class CheckIdCard {  
     10  private String cardNumber; // 完整的身份证号码  
     11  private Boolean cacheValidateResult = null; // 缓存身份证是否有效,因为验证有效性使用频繁且计算复杂  
     12  private Date cacheBirthDate = null; // 缓存出生日期,因为出生日期使用频繁且计算复杂  
     13  private final static String BIRTH_DATE_FORMAT = "yyyyMMdd"; // 身份证号码中的出生日期的格式  
     14  private final static Date MINIMAL_BIRTH_DATE = new Date(-2209017600000L); // 身份证的最小出生日期,1900年1月1日  
     15  private final static int NEW_CARD_NUMBER_LENGTH = 18;  
     16  private final static int OLD_CARD_NUMBER_LENGTH = 15;  
     17  private final static char[] VERIFY_CODE = { '1', '0', 'X', '9', '8', '7',  
     18    '6', '5', '4', '3', '2' }; // 18位身份证中最后一位校验码  
     19  private final static int[] VERIFY_CODE_WEIGHT = { 7, 9, 10, 5, 8, 4, 2, 1,  
     20    6, 3, 7, 9, 10, 5, 8, 4, 2 };// 18位身份证中,各个数字的生成校验码时的权值  
     21  public boolean validate() {  
     22   if (null == cacheValidateResult) {  
     23    boolean result = true;  
     24    result = result && (null != cardNumber); // 身份证号不能为空  
     25    result = result && NEW_CARD_NUMBER_LENGTH == cardNumber.length(); // 身份证号长度是18(新证)  
     26    // 身份证号的前17位必须是阿拉伯数字  
     27    for (int i = 0; result && i < NEW_CARD_NUMBER_LENGTH - 1; i++) {  
     28     char ch = cardNumber.charAt(i);  
     29     result = result && ch >= '0' && ch <= '9';  
     30    }  
     31    // 身份证号的第18位校验正确  
     32    result = result  
     33      && (calculateVerifyCode(cardNumber) == cardNumber  
     34        .charAt(NEW_CARD_NUMBER_LENGTH - 1));  
     35    // 出生日期不能晚于当前时间,并且不能早于1900年  
     36    try {  
     37     Date birthDate = this.getBirthDate();  
     38     result = result && null != birthDate;  
     39     result = result && birthDate.before(new Date());  
     40     result = result && birthDate.after(MINIMAL_BIRTH_DATE);  
     41     /** 
     42      * 出生日期中的年、月、日必须正确,比如月份范围是[1,12],日期范围是[1,31],还需要校验闰年、大月、小月的情况时, 
     43      * 月份和日期相符合 
     44      */  
     45     String birthdayPart = this.getBirthDayPart();  
     46     String realBirthdayPart = this.createBirthDateParser().format(  
     47       birthDate);  
     48     result = result && (birthdayPart.equals(realBirthdayPart));  
     49    } catch (Exception e) {  
     50     result = false;  
     51    }  
     52    cacheValidateResult = Boolean.valueOf(result);// TODO  
     53                // 完整身份证号码的省市县区检验规则  
     54   }  
     55   return cacheValidateResult;  
     56  }  
     57  /** 
     58   * 如果是15位身份证号码,则自动转换为18位 
     59   *  
     60   * @param cardNumber 
     61   * @return  
     62   */  
     63  public CheckIdCard(String cardNumber) {  
     64   if (null != cardNumber) {  
     65    cardNumber = cardNumber.trim();  
     66    if (OLD_CARD_NUMBER_LENGTH == cardNumber.length()) {  
     67     cardNumber = contertToNewCardNumber(cardNumber);  
     68    }  
     69   }  
     70   this.cardNumber = cardNumber;  
     71  }  
     72 public String getCardNumber() {  
     73   return cardNumber;  
     74  }  
     75  public String getAddressCode() {  
     76   this.checkIfValid();  
     77   return this.cardNumber.substring(0, 6);  
     78  }  
     79  public Date getBirthDate() {  
     80   if (null == this.cacheBirthDate) {  
     81    try {  
     82     this.cacheBirthDate = this.createBirthDateParser().parse(  
     83       this.getBirthDayPart());  
     84    } catch (Exception e) {  
     85     throw new RuntimeException("身份证的出生日期无效");  
     86    }  
     87   }  
     88   return new Date(this.cacheBirthDate.getTime());  
     89  }  
     90  public boolean isMale() {  
     91   return 1 == this.getGenderCode();  
     92  }  
     93  public boolean isFemal() {  
     94   return false == this.isMale();  
     95  }  
     96  /** 
     97   * 获取身份证的第17位,奇数为男性,偶数为女性 
     98   *  
     99   * @return 
    100   */  
    101  private int getGenderCode() {  
    102   this.checkIfValid();  
    103   char genderCode = this.cardNumber.charAt(NEW_CARD_NUMBER_LENGTH - 2);  
    104   return (((int) (genderCode - '0')) & 0x1);  
    105  }  
    106  private String getBirthDayPart() {  
    107   return this.cardNumber.substring(6, 14);  
    108  }  
    109  private SimpleDateFormat createBirthDateParser() {  
    110   return new SimpleDateFormat(BIRTH_DATE_FORMAT);  
    111  }  
    112  private void checkIfValid() {  
    113   if (false == this.validate()) {  
    114    throw new RuntimeException("身份证号码不正确!");  
    115   }  
    116  }  
    117  /** 
    118   * 校验码(第十八位数): 
    119   *  
    120   * 十七位数字本体码加权求和公式 S = Sum(Ai * Wi), i = 0...16 ,先对前17位数字的权求和; 
    121   * Ai:表示第i位置上的身份证号码数字值 Wi:表示第i位置上的加权因子 Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 
    122   * 2; 计算模 Y = mod(S, 11)< 通过模得到对应的校验码 Y: 0 1 2 3 4 5 6 7 8 9 10 校验码: 1 0 X 9 
    123   * 8 7 6 5 4 3 2 
    124   *  
    125   * @param cardNumber 
    126   * @return 
    127   */  
    128  private static char calculateVerifyCode(CharSequence cardNumber) {  
    129   int sum = 0;  
    130   for (int i = 0; i < NEW_CARD_NUMBER_LENGTH - 1; i++) {  
    131    char ch = cardNumber.charAt(i);  
    132    sum += ((int) (ch - '0')) * VERIFY_CODE_WEIGHT[i];  
    133   }  
    134   return VERIFY_CODE[sum % 11];  
    135  }  
    136  /** 
    137   * 把15位身份证号码转换到18位身份证号码<br> 
    138   * 15位身份证号码与18位身份证号码的区别为:<br> 
    139   * 1、15位身份证号码中,"出生年份"字段是2位,转换时需要补入"19",表示20世纪<br> 
    140   * 2、15位身份证无最后一位校验码。18位身份证中,校验码根据根据前17位生成 
    141   *  
    142   * @param cardNumber 
    143   * @return 
    144   */  
    145  private static String contertToNewCardNumber(String oldCardNumber) {  
    146   StringBuilder buf = new StringBuilder(NEW_CARD_NUMBER_LENGTH);  
    147   buf.append(oldCardNumber.substring(0, 6));  
    148   buf.append("19");  
    149   buf.append(oldCardNumber.substring(6));  
    150   buf.append(CheckIdCard.calculateVerifyCode(buf));  
    151   return buf.toString();  
    152  }
    153 }  
    CheckIdCard.java

    3). 一个 Action 类 TestValidationAction

     1 package com.struts.validation.app;
     2 
     3 import com.opensymphony.xwork2.ActionSupport;
     4 
     5 public class TestValidationAction extends ActionSupport{
     6 
     7     /**
     8      * 
     9      */
    10     private static final long serialVersionUID = 1L;
    11 
    12     private String idCard;
    13 
    14     @Override
    15     public String execute() throws Exception {
    16         return SUCCESS;
    17     }
    18 
    19     public String getIdCard() {
    20         return idCard;
    21     }
    22 
    23     public void setIdCard(String idCard) {
    24         this.idCard = idCard;
    25     }
    26 
    27     
    28 }
    TestValidationAction.java

    4). 一个实现类 IdCardValidator

     1 package com.struts.validation.app;
     2 
     3 import com.opensymphony.xwork2.validator.ValidationException;
     4 import com.opensymphony.xwork2.validator.validators.FieldValidatorSupport;
     5 
     6 public class IdCardValidator extends FieldValidatorSupport {
     7 
     8     @Override
     9     public void validate(Object object) throws ValidationException {
    10          // 1、获取请求字段名称及值
    11         String fieldName = getFieldName();
    12         Object fieldValue = getFieldValue(fieldName, object);
    13 
    14         // 2、验证参数值是否合法
    15         CheckIdCard validate = new CheckIdCard((String) fieldValue);
    16         Boolean result = false;
    17         try {
    18             result = validate.validate();
    19         } catch (Exception e) {
    20             result = false;
    21             e.printStackTrace();
    22         }
    23 
    24         // 3、如果验证失败,返回失败信息
    25         if (!result) {
    26             addFieldError(fieldName, object);
    27         }
    28 
    29     }
    30 
    31 }
    IdCardValidator.java

    5). 配置 validators.xml 文件 和 TestValidationAction-testValidation-validation.xml 文件

    <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE validators PUBLIC
              "-//Apache Struts//XWork Validator Definition 1.0//EN"
             "http://struts.apache.org/dtds/xwork-validator-definition-1.0.dtd">
     
      <!-- START SNIPPET: validators-default -->
     <validators>
          <validator name="idCard" class="com.struts.validation.app.IdCardValidator" />
     </validators>
    validators.xml
    <?xml version="1.0" encoding="utf-8" ?>
    
    <!DOCTYPE validators PUBLIC 
              "-//Apache Struts//XWork Validator 1.0.2//EN"
              "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
    
    <validators>
        <field name="idCard">
            <field-validator type="idCard">
                <message>身份证不正确!</message>
            </field-validator>
        </field>
    </validators>
    TestValidationAction-testValidation-validation.xml

    至此一个身份证验证器就完成了.

     

    文章未经版主同意不可任意转载,如有需要请标明文章出处。
  • 相关阅读:
    Oracle中查询表中数据的上次更新时间
    数据库分区 分库 分表 分片(转)
    beanFactory和factoryBean的区别(转)
    TCP三次握手形象理解
    深拷贝,浅拷贝(转)
    真的要去做
    '庞然大物'是怎么来的?为何有的技术明明看了很多遍依然不能很好的理解
    java6大原则之单一职责原则,里式替换原则
    状态不好记录一下
    java继承,多态
  • 原文地址:https://www.cnblogs.com/qihangzj/p/7337679.html
Copyright © 2011-2022 走看看