Struts之六 类型转换 & 输入验证

一、类型转换

1、继承ValidationAware 接口,进行错误捕捉

若 Action 类实现 ValidationAware 接口:Struts 在遇到类型转换错误时将不会继续调用其 Action 方法。Struts 将检查相关 action 元素的声明是否包含着一个 name=input 的 result.如果有, Struts 将把控制权转交给那个 result  元素; 若没有 input 结果, Struts 将抛出一个异常

//继承 ActionSupport,因为ActionSupport实现了ValidationAware 接口
public class ConversionAction extends ActionSupport

//在struts.xml中配置result input
<action name="testConversion" class="com.atguigu.struts2.app.ConversionAction">
<result>/success.jsp</result>
<result name="input">/index.jsp</result>
</action>


2、如何覆盖默认的错误消息?
1). 在对应的 Action 类所在的包中新建ActionClassName.properties 文件, ActionClassName 即为包含着输入字段的 Action 类的类名
2). 在属性文件中添加如下键值对: invalid.fieldvalue.fieldName=xxx

如:为Action类 ConversionAction.java新建配置文件ConversionAction.properties。
invalid.fieldvalue.age=请输入正确的年龄值.

3、simple 主题, 如何自动显示错误消息
1). 值栈的 Action(实现了 ValidationAware 接口) 对象中有一个  fieldErrors 属性.该属性的类型为 Map<String, List<String>> 键: 字段(属性名), 值: 错误消息组成的 List. 所以可以使用 LE 或 OGNL 的方式来显示错误消息: 
${fieldErrors.age[0]} 

2). 还可以使用 s:fielderror 标签来显示. 可以通过 fieldName 属性显示指定字段的错误.
<s:fielderror fieldName="age"></s:fielderror>

注:该消息显示在一个 ul, li, span 中. 如何去除 ul, li, span 呢 ?
在 template.simple 下面的 fielderror.ftl 定义了 simple 主题下, s:fielderror 标签显示错误消息的样式. 所以修改该
配置文件即可. 在 src 下新建  template.simple 包, 新建 fielderror.ftl 文件, 把原生的 fielderror.ftl 中的内容
复制到新建的 fielderror.ftl 中, 然后剔除 ul, li, span 部分即可. 

4. 如何自定义类型转换器

I.  开发类型转换器的类: 扩展 StrutsTypeConverter 类.

package com.iThings.struts.convertor;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

import org.apache.struts2.util.StrutsTypeConverter;

public class DateTypeConvertor extends StrutsTypeConverter {
	
	@Override
	public Object convertFromString(Map arg0, String[] arg1, Class arg2) {
		if(arg2 == Date.class){
			if(arg1!=null && arg1.length>0){
				String value = arg1[0];
				DateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
				try {
					return df.parseObject(value);
				} catch (ParseException e) {
					e.printStackTrace();
				}
			}
		}
		//若没有转换成功,则返回arg1
		return arg1;
	}

	@Override
	public String convertToString(Map arg0, Object arg1) {
		if(arg1 instanceof Date){
			DateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
			Date date = (Date)arg1;
			return df.format(date);
		}
		//若转换失败,则返回空
		return null;
	}

}


II. 配置类型转换器: 
有两种方式
①. 基于字段的配置: 
> 在字段所在的 Model(可能是 Action, 可能是一个 JavaBean) 的包下, 新建一个 ModelClassName-conversion.properties 文件
> 在该文件中输入键值对: fieldName=类型转换器的全类名.  java.util.Date = com.iThings.conversion.DateConverter
> 第一次使用该转换器时创建实例. 

> 类型转换器是单实例的!


Customer-conversion.properties

<span style="font-size:18px;">birth =com.iThings.struts.convertor.DateTypeConvertor</span>


②. 基于类型的配置:
> 在 src 下新建 xwork-conversion.properties
> 键入: 待转换的类型=类型转换器的全类名. java.util.Date = com.iThings.conversion.DateConverter
> 在当前 Struts2 应用被加载时创建实例. 

xwork-conversion.properties

<span style="font-size: 18px;">java.util.Date </span>= com.iThings.struts.convertor.DateTypeConvertor

5.类型转换器与复杂属性&集合协同使用


1)form 标签的 name 属性可以被映射到一个属性的属性. 
	<s:form action="testConversion">
		<s:textfield name="age" label="Age"></s:textfield>
		<s:textfield name="birth" label="Birth"></s:textfield>
		<s:textfield name="emp.firstName" label="FirstName"></s:textfield>
		<s:textfield name="emp.lastName" label="lastName"></s:textfield>
		<br><br>
		<s:submit></s:submit>
	</s:form>


2)Struts 还允许填充 Collection 里的对象, 这常见于需要快速录入批量数据的场合
<s:form action="testComplexPropertyAction">
		<s:textfield name="emps[0].firstName" label="firstName"></s:textfield>
		<s:textfield name="emps[0].lastName" label="lastName"></s:textfield>
		<s:textfield name="emps[1].firstName" label="firstName"></s:textfield>
		<s:textfield name="emps[1].lastName" label="lastName"></s:textfield>
		<s:textfield name="emps[2].firstName" label="firstName"></s:textfield>
		<s:textfield name="emps[2].lastName" label="lastName"></s:textfield>
		<s:submit></s:submit>
	</s:form>
	
	public class TestComplexPropertyAction extends ActionSupport {
	
		private static final long serialVersionUID = 1L;
		
		private Collection<Employee> emps = null;

		public Collection<Employee> getEmps() {
			return emps;
		}

		public void setEmps(Collection<Employee> emps) {
			this.emps = emps;
		}
		
		public String execute(){
			System.out.println(emps);
			return "success";
		}
	}
	

二、输入验证

1、声明式验证

I.  编写配置文件: 
> 把 struts-2.3.15.3\apps\struts2-blank\WEB-INF\classes\example 下的 Login-validation.xml 文件复制到当前 Action 所在的包下. 
> 把该配置文件改为: 把  Login 改为当前 Action 的名字. 
> 编写验证规则: 参见 struts-2.3.15.3/docs/WW/docs/validation.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>
     

     > 该错误消息国际化:在国际化资源文件 中加入一个键值对:

 error.int=Age needs to be between ${min} and ${max}

<message key="error.int"></message>. 
            
II. 若验证失败, 则转向 input 的那个 result. 所以需要配置 name=input 的 result
<result name="input">/validation.jsp</result>
 
III. 如何显示错误消息
> 若使用的是非 simple, 则自动显示错误消息.
> 若使用的是 simple 主题, 则需要 s:fielderror 标签或直接使用 EL 表达式(使用 OGNL)
${fieldErrors.age[0] } 
OR
<s:fielderror fieldName="age"></s:fielderror>

    注意: 可以为每一个不同的 action 请求定义其对应的验证文件: ActionClassName-AliasName-validation.xml
不带别名的配置文件: ActionClassName-validation.xml 中的验证规则依然会发生作用. 可以把各个 action 公有的验证规则配置在其中. 但需要注意的是, 只适用于某一个 action 的请求的验证规则就不要这里再配置了. 

Struts.xml文件中的配置:
			<action name="validation" class="com.iThings.validation.ValiditionAction" 
				method="execute">
				<result name="success">/Success.jsp</result>	
				<result name="input">/testValidation.jsp</result>	
			</action>
			<action name="validation2" class="com.iThings.validation.ValiditionAction" 
				method="execute">
				<result name="success">/Success.jsp</result>	
				<result name="input">/testValidation2.jsp</result>	
			</action>

验证配置文件命名(中间的别名为请求的action方法名)
ValidationAction-validation.xml
ValidationAction-validation-validation.xml
ValidationAction-validation2-validation.xml

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

		<!-- 设置短路验证: 若当前验证没有通过, 则不再进行下面的验证 -->
		<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">60</param>
			<message key="error.int"></message>
		</field-validator>	

3 修改包文件:若类型转换失败,则不执行输入验证


新建的包名:com.opensymphony.xwork2.interceptor;类名:ConversionErrorInterceptor
当类型转换失败时, 不再执行后续的验证拦截器, 而直接返回 input 的 result

<span style="white-space:pre">	</span>Object action = invocation.getAction();
        if (action instanceof ValidationAware) {
            ValidationAware va = (ValidationAware) action;

            if(va.hasFieldErrors() || va.hasActionErrors()){
            	return "input";
            }
        }


***************** 类ConversionErrorInterceptor.java********************

/*
 * Copyright 2002-2007,2009 The Apache Software Foundation.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.opensymphony.xwork2.interceptor;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ValidationAware;
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.commons.lang3.StringEscapeUtils;

import java.util.HashMap;
import java.util.Map;


/**
 * <!-- START SNIPPET: description -->
 * ConversionErrorInterceptor adds conversion errors from the ActionContext to the Action's field errors.
 *
 * <p/>
 * This interceptor adds any error found in the {@link ActionContext}'s conversionErrors map as a field error (provided
 * that the action implements {@link ValidationAware}). In addition, any field that contains a validation error has its
 * original value saved such that any subsequent requests for that value return the original value rather than the value
 * in the action. This is important because if the value "abc" is submitted and can't be converted to an int, we want to
 * display the original string ("abc") again rather than the int value (likely 0, which would make very little sense to
 * the user).
 *
 *
 * <!-- END SNIPPET: description -->
 *
 * <p/> <u>Interceptor parameters:</u>
 *
 * <!-- START SNIPPET: parameters -->
 *
 * <ul>
 *
 * <li>None</li>
 *
 * </ul>
 *
 * <!-- END SNIPPET: parameters -->
 *
 * <p/> <u>Extending the interceptor:</u>
 *
 * <p/>
 *
 * <!-- START SNIPPET: extending -->
 *
 * Because this interceptor is not web-specific, it abstracts the logic for whether an error should be added. This
 * allows for web-specific interceptors to use more complex logic in the {@link #shouldAddError} method for when a value
 * has a conversion error but is null or empty or otherwise indicates that the value was never actually entered by the
 * user.
 *
 * <!-- END SNIPPET: extending -->
 *
 * <p/> <u>Example code:</u>
 *
 * <pre>
 * <!-- START SNIPPET: example -->
 * <action name="someAction" class="com.examples.SomeAction">
 *     <interceptor-ref name="params"/>
 *     <interceptor-ref name="conversionError"/>
 *     <result name="success">good_result.ftl</result>
 * </action>
 * <!-- END SNIPPET: example -->
 * </pre>
 *
 * @author Jason Carreira
 */
public class ConversionErrorInterceptor extends AbstractInterceptor {

    public static final String ORIGINAL_PROPERTY_OVERRIDE = "original.property.override";

    protected Object getOverrideExpr(ActionInvocation invocation, Object value) {
        return escape(value);
    }

    protected String escape(Object value) {
        return "\"" + StringEscapeUtils.escapeJava(String.valueOf(value)) + "\"";
    }

    @Override
    public String intercept(ActionInvocation invocation) throws Exception {

        ActionContext invocationContext = invocation.getInvocationContext();
        Map<String, Object> conversionErrors = invocationContext.getConversionErrors();
        ValueStack stack = invocationContext.getValueStack();

        HashMap<Object, Object> fakie = null;

        for (Map.Entry<String, Object> entry : conversionErrors.entrySet()) {
            String propertyName = entry.getKey();
            Object value = entry.getValue();

            if (shouldAddError(propertyName, value)) {
                String message = XWorkConverter.getConversionErrorMessage(propertyName, stack);

                Object action = invocation.getAction();
                if (action instanceof ValidationAware) {
                    ValidationAware va = (ValidationAware) action;
                    va.addFieldError(propertyName, message);
                }

                if (fakie == null) {
                    fakie = new HashMap<Object, Object>();
                }

                fakie.put(propertyName, getOverrideExpr(invocation, value));
            }
        }

        if (fakie != null) {
            // if there were some errors, put the original (fake) values in place right before the result
            stack.getContext().put(ORIGINAL_PROPERTY_OVERRIDE, fakie);
            invocation.addPreResultListener(new PreResultListener() {
                public void beforeResult(ActionInvocation invocation, String resultCode) {
                    Map<Object, Object> fakie = (Map<Object, Object>) invocation.getInvocationContext().get(ORIGINAL_PROPERTY_OVERRIDE);

                    if (fakie != null) {
                        invocation.getStack().setExprOverrides(fakie);
                    }
                }
            });
        }
        
        Object action = invocation.getAction();
        if (action instanceof ValidationAware) {
            ValidationAware va = (ValidationAware) action;

            if(va.hasFieldErrors() || va.hasActionErrors()){
            	return "input";
            }
        }
        
        return invocation.invoke();
    }

    protected boolean shouldAddError(String propertyName, Object value) {
        return true;
    }
}



4 非字段验证 expressionValidition
     <validator type="expression">
        <param name="expression"><![CDATA[password==password2]]></param>
        <message>Password is not equals to password2</message>
     </validator>

显示非字段验证的错误消息, 使用 s:actionerror 标签:  <s:actionerror/>


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

	i18n.properties中的配置:
	error.int=${getText(fieldName)} needs to be between ${min} and ${max}

	age=age
	count=count   


6 自定义验证器
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. 实例
1)实现Validator 

***************  IDCard.java *******************

package com.iThings.validation;

public class IDCard {
	final int[] wi = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2, 1 };
	final int[] vi = { 1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2 };
	private int[] ai = new int[18];

	public IDCard() {}

	public boolean Verify(String idcard) {
		if (idcard.length() == 15) {
			idcard = uptoeighteen(idcard);
		}
		if (idcard.length() != 18) {
			return false;
		}
		String verify = idcard.substring(17, 18);
		if (verify.equals(getVerify(idcard))) {
			return true;
		}
		return false;
	}

	public String getVerify(String eightcardid) {
		int remaining = 0;

		if (eightcardid.length() == 18) {
			eightcardid = eightcardid.substring(0, 17);
		}

		if (eightcardid.length() == 17) {
			int sum = 0;
			for (int i = 0; i < 17; i++) {
				String k = eightcardid.substring(i, i + 1);
				ai[i] = Integer.parseInt(k);
			}

			for (int i = 0; i < 17; i++) {
				sum = sum + wi[i] * ai[i];
			}
			remaining = sum % 11;
		}

		return remaining == 2 ? "X" : String.valueOf(vi[remaining]);
	}

	public String uptoeighteen(String fifteencardid) {
		String eightcardid = fifteencardid.substring(0, 6);
		eightcardid = eightcardid + "19";
		eightcardid = eightcardid + fifteencardid.substring(6, 15);
		eightcardid = eightcardid + getVerify(eightcardid);
		return eightcardid;
	}
	
	public static void main(String[] args) {
		
		String idcard1 = "350211197607142059"; 
		String idcard2 = "350211197607442059";
		
		IDCard idcard = new IDCard(); 
		System.out.println(idcard.Verify(idcard1)); 
		System.out.println(idcard.Verify(idcard2)); 
	}

}

***************  IDCardValidator.java *******************

package com.iThings.validation;

import com.opensymphony.xwork2.validator.ValidationException;
import com.opensymphony.xwork2.validator.validators.FieldValidatorSupport;

public class IDCardValidator extends FieldValidatorSupport {

	@Override
	public void validate(Object object) throws ValidationException {
		//1. 获取字段的名字和值
		String fieldName = getFieldName();
        Object value = this.getFieldValue(fieldName, object);
        
		//2. 验证
		IDCard idCard = new IDCard();
		boolean result = idCard.Verify((String)value);
        
		//3. 若验证失败, 则 ...
		if(!result){
			addFieldError(fieldName, object);
		}
	}
}




2)在src目录下新建validators.xml文件。配置验证器

***************  validators.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.atguigu.struts2.validation.app.IDCardValidator"/>
</validators>



3)在验证配置文件中,使用该验证器(同一般验证器一样)

		<field name="idCard">
			<field-validator type="idcard">
				<message>It is not a idcard</message>
			</field-validator>
		</field>


***************  IDCardValidator.java IDCard.java *******************

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值