基本程序结构
- 拦截器在实际开发过程之中有一个实际的用处:登录检测,数据验证.虽然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类的参数.
- 通过ActionInvocation类的getAction()方法:取得请求调用的Action对象
- 从request请求路径中取得执行的方法名称(!之后 .之前)
- 有了Action对象就可以取得Action执行的业务方法名称
- 拼凑出验证规则的"key"
- 通过反射取得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;
}
各项数据验证
数据验证的处理操作
- 取得所有的输入参数
- 取得参数名称
-
方式一:拆分出所有规则,而后使用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;
}
}
- 执行请求
- 后台输出的结果
- 发现在接受的参数中可能会得到数组
- 编写一个专门负责验证检测的工具类
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()方法可以驱除全部的错误信息.
-
思路:
- 通过反射取得ActionSupport类的addFieldErrord()方法:用于保存错误信息
- 通过反射取得ActionSupport类的getText()方法:取得资源文件中的内容
- 当验证出错的时候保存两个信息key=参数名称,value=错误提示信息
- 取得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
- 后台输出,成功拦截