struts2---服务器端验证

基本程序结构

  • 拦截器在实际开发过程之中有一个实际的用处:登录检测,数据验证.虽然struts2以提供了一系列的验证操作,这些验证并不好使用.
  • 因为当表单的参数类型输入错误的时候(例如:int型输入了一个String型),后台会输出错误,而当类型出现错误的时候会默认跳转到"input"页面中

在这里插入图片描述

  • 由于其实在数据接收之后再进行验证处理,所以在实际开发中不可能使用.最好的做法时在数据接收之前进行验证操作.

在这里插入图片描述

  • 准备出一个代码的雏形
  • 编写Message的vo类
package mao.shu.vo;

import java.util.Date;

public class Message {
	private int mid;
	private String info;
	private Double price;
	private Date pubDate;
	//getter和setter方法
}
  • 定义一个MessageAction程序类
package mao.shu.action;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;
import mao.shu.vo.*;

public class MessageAction extends ActionSupport{
	private Message msg = new Message();
	public void setMsg(Message msg) {
		this.msg = msg;
	}
	public Message getMsg() {
		return msg;
	}
	
	public String add(){
		System.out.println("新对象实例化成功********");
		System.out.println(this.msg);
		return Action.SUCCESS;
	}
	public String edit(){
		System.out.println("对象实修改成功#############");
		System.out.println(this.msg);
		return Action.SUCCESS;
	}
}

  • 编写struts.xml文件,去掉所有的拦截器和拦截器栈的配置
  • 为MessageAction配置映射路径,并配置MessageAction类的业务方法执行失败之后的跳转路径
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
	"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
	 <package name="root" namespace="/" extends="struts-default">
	 
	 <global-results><!--  拦截器统一的跳转路径-->
	 	<result name="login">/login.jsp</result>
	 	<result name="error">/error.jsp</result>
	 </global-results>
	    
        <action name="MessageAction" class="mao.shu.action.MessageAction">
        	<result name="add.error">/message_add.jsp</result><!--add()业务方法执行失败跳转的页面  -->
        	<result name="edit.error">/message_list.jsp</result><!-- edit()方法执行失败跳转的页面 -->
     </action>  
     
 </package>
</struts>

  • 既然需要有一个专门的验证规则文件,所以应该定义在资源文件之中
  • 定义struts.properties文件
struts.custom.i18n.resources=Messages,Pages
  • 对于Messages文件里面需要定义一些公共的提示信息
validate.string.error=该数据不允许为空
validate.int.error=该数据必须是数字!
validate.double.error=该数据必须是数字
validate.date.error=该数据必须为日期
  • 对于MessageAction编写验证规则
  • 编写Validators.properties文件,该文件描述的是Action业务方法的验证规则
    • 其中key的格式为"Action类名称.业务方法名称.rules",
    • value的值是对Action类字段的属性的类型描述,其中字段名称(msg)必须与Action类中的字段名称一致
MessageAction.add.rules=msg.info:string|msg.price:double|msg.pubDate:date
MessageAction.edit.rules=msg.mid:int|msg.info:string|msg.price:double|msg.pubDate:date
  • 编写error.page文件,该文件在Action类的方法执行出错的时候显示错误信息,struts2中有一个错误的集合信息"allFieldErrors",最好将所有的错误信息都保存在这个集合中,避免重新使用其他集合处理的操作
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!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>
	<h1>程序出现了错误!</h1>
	${allFieldErrors }
</body>
</html>

验证规则的定义和读取

  • 验证规则描述在一个资源文件中,资源文件的读取方法在ActionSupport类中定义—getText(),但如何在拦截器中取得验证规则?
  • 在拦截器中取得规则的思路
  • 在struts2中每一个自定义的拦截器都需要继承自AbstractInterceptor接口,并且复写intercept()方法,这个方法中有一个ActionInvocation类的参数.
  1. 通过ActionInvocation类的getAction()方法:取得请求调用的Action对象
  2. 从request请求路径中取得执行的方法名称(!之后 .之前)
  3. 有了Action对象就可以取得Action执行的业务方法名称
  4. 拼凑出验证规则的"key"
  5. 通过反射取得Action的"getText()"方法
  • 编写ValidatorInterceptor拦截器,在此之前需要将struts.properties文件中设置好Validators.properties文件的引用
package mao.shu.util.interceptor;

import java.lang.reflect.Method;

import javax.servlet.ServletContext;
import javax.swing.plaf.basic.BasicSliderUI.ActionScroller;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class ValidatorInterceptor extends AbstractInterceptor {

	@Override
	public String intercept(ActionInvocation arg0) throws Exception {
		Object actionObject = arg0.getAction();//取得调用的Action类对象
		String uri = ServletActionContext.getRequest().getRequestURI();//得到请求路径uri
		//拆分处调用的Action类的方法名称
		String actionMethodName = uri.substring(uri.lastIndexOf("!")+1, uri.lastIndexOf("."));
		//拼凑出validators资源文件中的规则"key"
		String rulesKey = actionObject.getClass().getSimpleName()+"."+actionMethodName+".rules";
		
		//得到读取资源文件的方法
		Method getMethod = actionObject.getClass().getMethod("getText",String.class,String.class);
		//取得验证规则的内容,如果没有验证规则,使用"null"代替
		String rulesValue = (String)getMethod.invoke(actionObject, rulesKey,"null");
		System.out.println(rulesValue);
		return null;
	}

}

  • 此时有个问题出现
    • 目前的代码都是在拦截器中完成,代码太乱了,最好将这些操作保存在一个工具类中"GetResourcesRules"
package mao.shu.util.action;

import java.lang.reflect.Method;

import org.apache.struts2.ServletActionContext;

public class GetResourcesRules {
	public static String getRulesValue(Object actionObject) throws Exception{
		String uri = ServletActionContext.getRequest().getRequestURI();//得到请求路径uri
		//拆分处调用的Action类的方法名称
		String actionMethodName = uri.substring(uri.lastIndexOf("!")+1, uri.lastIndexOf("."));
		if(actionMethodName != null){
			//拼凑出validators资源文件中的规则"key"
			String rulesKey = actionObject.getClass().getSimpleName()+"."+actionMethodName+".rules";
			
			//得到读取资源文件的方法
			Method getMethod = actionObject.getClass().getMethod("getText",String.class,String.class);
			//取得验证规则的内容,如果没有验证规则,使用"null"代替
			String rulesValue = (String)getMethod.invoke(actionObject, rulesKey,"null");
			return rulesValue;
		}
		return null;
	}
}

  • 拦截器的类就相当于是一个客户端,既然是客户端,那么在处理的时候就必须简化调用
	@Override
	public String intercept(ActionInvocation arg0) throws Exception {
		Object actionObject = arg0.getAction();//取得调用的Action类对象
		String rulesValue = GetResourcesRules.getRulesValue(actionObject);
		if(rulesValue != null){
			
		}
		return null;
	}

各项数据验证

数据验证的处理操作

  1. 取得所有的输入参数
  2. 取得参数名称
  • 方式一:拆分出所有规则,而后使用reqeust对象一次取出每一个参数,而后对参数类型进行验证.

  • 方式二:在struts2中帮助用户接受了所有参数在intercept()方法中的ActionInvocation接口对象中提供有一个取得ActionContext类的操作对象方法—getInvocationContext(),通过这个对象的getParameters()方法可以取得全部的提交参数

  • 在ValidatotInterceptor拦截器中测试接收内容

package mao.shu.util.interceptor;

import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.swing.plaf.basic.BasicSliderUI.ActionScroller;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;


import mao.shu.util.action.GetResourcesRules;

public class ValidatorInterceptor extends AbstractInterceptor {

	@Override
	public String intercept(ActionInvocation arg0) throws Exception {
		Object actionObject = arg0.getAction();//取得调用的Action类对象
		String rulesValue = GetResourcesRules.getRulesValue(actionObject);
		if(rulesValue != null){
			Map<String,Object> allParams = arg0.getInvocationContext().getParameters();
			Iterator<Map.Entry<String, Object>> iter = allParams.entrySet().iterator();
			while(iter.hasNext()){
				Map.Entry<String, Object> temp = iter.next();
				System.out.println("key = "+temp.getKey()+" : value = "+temp.getValue());
			}
		}
		return null;
	}

}

  • 执行请求

http://localhost:8080/InterceptorProject/MessageAction!add.action?msg.mid=1&msg.info=你好&msg.price=55.5&msg.pubDate=2019-01-18

  • 后台输出的结果

在这里插入图片描述

  • 发现在接受的参数中可能会得到数组
  • 编写一个专门负责验证检测的工具类
package mao.shu.util;

public class ValidateData {
	public static boolean isString(String arg){
		if(arg == null || "".equals(arg)){
			return false;
		}
		return true;
		
	}
	public static boolean isStrings(String[] args){
		for(int x = 0; x < args.length;x ++){
				if(!isString(args[x])){
					return false;
				}
		}
		return true;
	}
	/**
	 * 验证一个字符串是否符合一个正则表达式
	 * @param data
	 * @param parame
	 * @return
	 */
	public static boolean validateRegex(String data,String regex){
		if(isString(data)){
			return data.matches(regex);
		}
		return false;
	}
	
	public static boolean isInt(String str){
		return validateRegex(str, "\\d+");
	}
	public static boolean isInts(String[] strs){
		for(int x =0; x < strs.length; x++){
			if(!isInt(strs[x])){
				return false;
			}
		}
		return true;
	}
	
	public static boolean isDouble(String str){
		return validateRegex(str, "\\d+(\\.\\d+)?");
	}
	public static boolean isDoubles(String[] strs){
		for(int x =0; x < strs.length; x++){
			if(!isDouble(strs[x])){
				return false;
			}
		}
		return true;
	}
	
	/**
	 * 判断字符串是否为日期的方法,该日期不包含时间部分 如:yyyy-dd-mm
	 * @param str
	 * @return
	 */
	public static boolean isDate(String str){
		return validateRegex(str, "\\d{4}-\\d{2}-\\d{2}");
	}
	public static boolean isDates(String[] strs){
		for(int x =0; x < strs.length; x++){
			if(!isDate(strs[x])){
				return false;
			}
		}
		return true;
	}
	
	/**
	 * 判断一个字符串是否为日期时间的方法,包含日期时间部分例如:yyyy-dd-mm HH:mm:ss
	 * @param str
	 * @return
	 */
	public static boolean isDateTime(String str){
		return validateRegex(str, "\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}");
	}
	public static boolean isDateTimes(String[] strs){
		for(int x =0; x < strs.length; x++){
			if(!isDateTime(strs[x])){
				return false;
			}
		}
		return true;
	}
}

  • 验证的处理不应该直接在拦截器中的处理,应该在建立一个类:ActionValidator这个类专门负责验证参数.
  • 设计雏形
package mao.shu.util.action;

import java.util.Iterator;
import java.util.Map;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;

import mao.shu.util.ValidateData;

public class ActionValidate {
	/**
	 * 对指定Action参数进行验证
	 * @param actionobject 调用的Action类对象
	 * @param arg0 
	 * @param rules 规则字符串 格式为 参数名称:类型|参数名称:类型|......
	 * @return
	 * @throws Exception
	 */
	public static boolean validateParams(Object actionobject,ActionInvocation arg0,String rules)throws Exception{
			Map<String,Object> allParams = arg0.getInvocationContext().getParameters();
			//拆分出规则
			String[] allRules = rules.split("\\|");
			//再拆分出 参数类型和类型
			for(int x = 0; x < allRules.length; x++){
				String [] temp = allRules[x].split(":");
				String paramValue = null;//保存单个参数内容的情况
				String[] paramValues = null;//保存多个参数内容的情况
				//根据参数名称得到参数内容

					if(temp[0].endsWith("Arrays")){//处理参数为数组的清情况
						try{
							paramValues = (String[])allParams.get(temp[0]);
						}catch(Exception e){}
						switch(temp[1]){
						case "string" : {
							if(!ValidateData.isStrings(paramValues)){
								System.out.println(temp[0] + " 不是字符串");
							}
							break;
						}
						case "int" : {
							if(!ValidateData.isInts(paramValues)){
								System.out.println(temp[0] + " 不是整形");
							}
							break;
						}
						case "double" : {
							if(!ValidateData.isDoubles(paramValues)){
								System.out.println(temp[0] + " 不是浮点型");
							}
							break;
						}
						case "date" : {
							if(!ValidateData.isDates(paramValues)){
								System.out.println(temp[0] + " 不是日期");
							}
							break;
						}
						case "datetime" : {
							if(!ValidateData.isDateTime(paramValue)){
								System.out.println(temp[0] + " 不是日期时间");
							}
							break;
						}
						
					}
					}else{//处理数组为单个参数的清空
						try{
							paramValue = ((String[])allParams.get(temp[0]))[0];
						}catch(Exception e){}
						switch(temp[1]){
						case "string" : {
							if(!ValidateData.isString(paramValue)){
								System.out.println(temp[0] + " 不是字符串");
							}
							break;
						}
						case "int" : {
							if(!ValidateData.isInt(paramValue)){
								System.out.println(temp[0] + " 不是整形");
							}
							break;
						}
						case "double" : {
							if(!ValidateData.isDouble(paramValue)){
								System.out.println(temp[0] + " 不是浮点型");
							}
							break;
						}
						case "date" : {
							if(!ValidateData.isDate(paramValue)){
								System.out.println(temp[0] + " 不是日期");
							}
							break;
						}
						case "datetime" : {
							if(!ValidateData.isDateTime(paramValue)){
								System.out.println(temp[0] + " 不是日期时间");
							}
							break;
						}
						
					}
					}
			
				
				
			}
			return false;
		}
	
}


  • 如果验证的参数为数组的情况,则在编写规则的时候,需要在参数名称后面加上"Arrays"符号,例如在Validator.properties文件中添加以下的验证规则:
MessageAction.rm.rules=msg.midArrays:int
  • 测试访问

http://localhost:8080/InterceptorProject/MessageAction!rm.action?msg.midArrays=错误的字符串

  • 后台输出结果

在这里插入图片描述

保存错误信息

  • 如果真的出现错误最好的做法是依据Action的过程进行处理,Action中所有的错误信息通过addFieldsErrors()方法保存.而且通过getFieldErrors()方法可以驱除全部的错误信息.

  • 思路:

    1. 通过反射取得ActionSupport类的addFieldErrord()方法:用于保存错误信息
    2. 通过反射取得ActionSupport类的getText()方法:取得资源文件中的内容
    3. 当验证出错的时候保存两个信息key=参数名称,value=错误提示信息
    4. 取得ActionSupport类的getFieldErrors()方法:如果这个map集合中有内容表示有错误,如果map集合.size==0表示没有错误.
  • 完善ActionValidate类中的方法

package mao.shu.util.action;

import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;

import mao.shu.util.ValidateData;

public class ActionValidate {
	/**
	 * 对指定Action参数进行验证
	 * @param actionobject 调用的Action类对象
	 * @param arg0 
	 * @param rules 规则字符串 格式为 参数名称:类型|参数名称:类型|......
	 * @return
	 * @throws Exception
	 */
	public static boolean validateParams(Object actionobject,ActionInvocation arg0,String rules)throws Exception{
			Map<String,Object> allParams = arg0.getInvocationContext().getParameters();
			//取得设置错误信息的方法
			Method addFieldErrorsMethod = actionobject.getClass().getMethod("addFieldError", String.class,String.class);
			//取得错误集合的方法
			Method getFieldErrorsMethod = actionobject.getClass().getMethod("getFieldErrors");
			//取得读取资源文件信息的方法
			Method getTextMethod = actionobject.getClass().getMethod("getText",String.class);
			//拆分出规则
			String[] allRules = rules.split("\\|");
			//再拆分出 参数类型和类型
			for(int x = 0; x < allRules.length; x++){
				String [] temp = allRules[x].split(":");
				String paramValue = null;//保存单个参数内容的情况
				String[] paramValues = null;//保存多个参数内容的情况
				//根据参数名称得到参数内容

					if(temp[0].endsWith("Arrays")){//处理参数为数组的清情况
						try{
							paramValues = (String[])allParams.get(temp[0]);
						}catch(Exception e){}
						switch(temp[1]){
						case "string" : {
							if(!ValidateData.isStrings(paramValues)){
								String message = (String)getTextMethod.invoke(actionobject, "validate.string.error");
								addFieldErrorsMethod.invoke(actionobject,temp[0],message);
							}
							break;
						}
						case "int" : {
							if(!ValidateData.isInts(paramValues)){
								String message = (String)getTextMethod.invoke(actionobject, "validate.int.error");
								addFieldErrorsMethod.invoke(actionobject,temp[0],message);
							}
							break;
						}
						case "double" : {
							if(!ValidateData.isDoubles(paramValues)){
								String message = (String)getTextMethod.invoke(actionobject, "validate.double.error");
								addFieldErrorsMethod.invoke(actionobject,temp[0],message);
							}
							break;
						}
						case "date" : {
							if(!ValidateData.isDates(paramValues)){
								String message = (String)getTextMethod.invoke(actionobject, "validate.date.error");
								addFieldErrorsMethod.invoke(actionobject,temp[0],message);
							}
							break;
						}
						case "datetime" : {
							if(!ValidateData.isDateTime(paramValue)){
								String message = (String)getTextMethod.invoke(actionobject, "validate.datetime.error");
								addFieldErrorsMethod.invoke(actionobject,temp[0],message);
							}
							break;
						}
						
					}
					}else{//处理数组为单个参数的清空
						try{
							paramValue = ((String[])allParams.get(temp[0]))[0];
						}catch(Exception e){}
						switch(temp[1]){
						case "string" : {
							if(!ValidateData.isString(paramValue)){
								String message = (String)getTextMethod.invoke(actionobject, "validate.string.error");
								addFieldErrorsMethod.invoke(actionobject,temp[0],message);
							}
							break;
						}
						case "int" : {
							if(!ValidateData.isInt(paramValue)){
								String message = (String)getTextMethod.invoke(actionobject, "validate.int.error");
								addFieldErrorsMethod.invoke(actionobject,temp[0],message);
							}
							break;
						}
						case "double" : {
							if(!ValidateData.isDouble(paramValue)){
								String message = (String)getTextMethod.invoke(actionobject, "validate.double.error");
								addFieldErrorsMethod.invoke(actionobject,temp[0],message);
							}
							break;
						}
						case "date" : {
							if(!ValidateData.isDate(paramValue)){
								String message = (String)getTextMethod.invoke(actionobject, "validate.date.error");
								addFieldErrorsMethod.invoke(actionobject,temp[0],message);
							}
							break;
						}
						case "datetime" : {
							if(!ValidateData.isDateTime(paramValue)){
								String message = (String)getTextMethod.invoke(actionobject, "validate.datetime.error");
								addFieldErrorsMethod.invoke(actionobject,temp[0],message);
							}
							break;
						}
						
					}
					}
			
				
				
			}
				System.out.println(fieldErrors);
			Map<String,List<String>> fieldErrors = (Map<String,List<String>>)getFieldErrorsMethod.invoke(actionobject);
			return fieldErrors.size() == 0;
		}
	
}

  • 完善拦截器的方法,如果验证出现出错,返回到方法执行错误的页面中
    • 错误页面在struts.xml文件中配置的名称为"方法名称"+".error"
    • 因此需要先得到调用的方法名称
    • 方法名称在请求的路径之中,考虑到地方可能也会使用到该方法,所以可以将取得方法名称的操作写在工具类:GetResourcesRules之中
	/**
	 * 得到请求路径中Action调用的方法名称
	 * @return
	 * @throws Exception
	 */
	public static String getActionMethodName()throws Exception{
		String uri = ServletActionContext.getRequest().getRequestURI();//得到请求路径uri
		//拆分处调用的Action类的方法名称
		String actionMethodName = uri.substring(uri.lastIndexOf("!")+1, uri.lastIndexOf("."));
		return actionMethodName;
	}
  • 完善拦截器的intercept()方法

public class ValidatorInterceptor extends AbstractInterceptor {

	@Override
	public String intercept(ActionInvocation arg0) throws Exception {
		Object actionObject = arg0.getAction();//取得调用的Action类对象
		String rulesValue = GetResourcesRules.getRulesValue(actionObject);
	
			//如果参数验证正确
			if(ActionValidate.validateParams(actionObject, arg0, rulesValue)){
				return arg0.invoke();
			}else{
				return GetResourcesRules.getActionMethodName() + ".error";
			}
	}

}
  • 修改struts.xml文件的配置,将ValidatorInterceptor拦截器和struts中defaultStack拦截器配置在一起使用,
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
	"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
	 <package name="root" namespace="/" extends="struts-default">
	 <interceptors>
	 	<interceptor name="Validator" class="mao.shu.util.interceptor.ValidatorInterceptor"/>
	 	<interceptor-stack name="maoshu">
	 		<interceptor-ref name="Validator"/>
	 		<interceptor-ref name="defaultStack"/>
	 	</interceptor-stack>
	 </interceptors>
	 
	 <global-results><!--  拦截器统一的跳转路径-->
	 	<result name="login">/login.jsp</result>
	 	<result name="error">/error.jsp</result>
	 </global-results>
	    
        <action name="MessageAction" class="mao.shu.action.MessageAction">
        	<interceptor-ref name="maoshu"/>
        	<result name="add.error">/message_add.jsp</result><!--add()业务方法执行失败跳转的页面  -->
        	<result name="edit.error">/message_list.jsp</result><!-- edit()方法执行失败跳转的页面 -->
     </action>  
     
 </package>
</struts>

  • 重新测试请求

http://localhost:8080/InterceptorProject/MessageAction!add.action

  • 后台输出,成功拦截

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值