Struts2 修改验证器源码,能够区分GET、POST请求

软考过后,有些闲工夫,打算捡起之前所学的Struts2研究研究源码。


一、抛出问题

关于表单显示页面跟表单请求页面的地址

通常情况下来讲,表单的展示地址即 get 请求表单页地址跟表单 post 提交的地址是不同的。一个是表单的展示,一个是表单提交数据请求的处理,但是这里往往会遇到表单验证不通过回显的问题。在 Struts2 中可以对不同的 action method 渲染同一个 result,那么就会变成表单提交前,表单提交后回显的页面地址不同,但是页面内容大致一样(多出来一些验证不通过的 fieldErrors 内容)

如何达到请求与回显表单地址一致?


二、思考

  1. 首先 Struts2 验证器实现有两种方式,一种是声明式、一种是继承 Validateable 接口,复写 validate() 方法、还有自定义验证器(不讨论)
  2. 这两种方式中第二种无疑可以通过 ServletActionContext.getRequest().getMethod() 获取请求类型进行判断实现上面的问题
  3. 设法通过声明式的方式实现会更有价值(后面会说价值所在)



三、实现


如何实现?

声明中验证文件的名称为 ActionClassName-[ActionName]-validation.xml 
文件名中加入带有 get、post 的固定字段就能够区分请求的类型验证文件


步骤

要知道一些基本的 Struts2 的运行原理。
通过 struts-default.xml 的配置中找到拦截器栈,从中找到验证拦截器


在该类中找到 doIntercept 方法,并找到跟验证相关的方法 super.doIntercept();



进入该方法中找到相关的验证代码段,如下两种图片所示, actionValiditorManager 为验证操作的对象




private ActionValidatorManager actionValidatorManager;  是接口,通过 debug 断点或者 @Inject 注入注解排查该接口的实现类是 AnnotationActionValidatorManager 

如下图所示:




进入 AnnotationActionValidatorManager ,具体该类如何工作试用 debug 调试,并且其中的代码并不是很难,这里略过...


修改源码

要修改的地方有两个部分,

一个是在验证器在组装文件名时加入我们想要的文件名

另一个是在验证器获取、存入缓存中的 Key 需要修改

首先针对 buildValidatorKey 该方法修,只是在原来存储的名字上增加了请求方式,因为验证的xml文件只会获取一次,不管是获取到了get或者是获取到了post,在存储的缓存中有一个 Key 想对应,但是原有的 Key 并没有区分请求的方式,这样无法通过请求方式获取到相应的验证文件中的规则,改如下:

	protected String buildValidatorKey(Class clazz, String context) {
		ActionInvocation invocation = ActionContext.getContext().getActionInvocation();
		ActionProxy proxy = invocation.getProxy();
		ActionConfig config = proxy.getConfig();

		StringBuilder sb = new StringBuilder(clazz.getName());
		// 新增加代码,拼接请求方式,并且加入到缓存中
		String method = ServletActionContext.getRequest().getMethod();
		if (method != null && method.length() > 0) {
			sb.append("."+method.toLowerCase());
		}
		sb.append("/");
		if (StringUtils.isNotBlank(config.getPackageName())) {
			sb.append(config.getPackageName());
			sb.append("/");
		}

		// the key needs to use the name of the action from the config file,
		// instead of the url, so wild card actions will have the same validator
		// see WW-2996

		// UPDATE:
		// WW-3753 Using the config name instead of the context only for
		// wild card actions to keep the flexibility provided
		// by the original design (such as mapping different contexts
		// to the same action and method if desired)

		// UPDATE:
		// WW-4536 Using NameVariablePatternMatcher allows defines actions
		// with patterns enclosed with '{}', it's similar case to WW-3753
		String configName = config.getName();
		if (configName.contains(ActionConfig.WILDCARD) || (configName.contains("{") && configName.contains("}"))) {
			sb.append(configName);
			sb.append("|");
			sb.append(proxy.getMethod());
		} else {
			sb.append(context);
		}

		return sb.toString();
	}



验证器在获取验证文件内容时需要通过请求的方式区别,通过修改如下代码实现区分:

	private List<ValidatorConfig> buildValidatorConfigs(Class clazz, String context, boolean checkFile,
			Set<String> checked) {
		List<ValidatorConfig> validatorConfigs = new ArrayList<ValidatorConfig>();

		if (checked == null) {
			checked = new TreeSet<String>();
		} else if (checked.contains(clazz.getName())) {
			return validatorConfigs;
		}

		if (clazz.isInterface()) {
			Class[] interfaces = clazz.getInterfaces();

			for (Class anInterface : interfaces) {
				validatorConfigs.addAll(buildValidatorConfigs(anInterface, context, checkFile, checked));
			}
		} else {
			if (!clazz.equals(Object.class)) {
				validatorConfigs.addAll(buildValidatorConfigs(clazz.getSuperclass(), context, checkFile, checked));
			}
		}

		// look for validators for implemented interfaces
		Class[] interfaces = clazz.getInterfaces();

		for (Class anInterface1 : interfaces) {
			if (checked.contains(anInterface1.getName())) {
				continue;
			}

			validatorConfigs.addAll(buildClassValidatorConfigs(anInterface1, checkFile));

			if (context != null) {
				validatorConfigs.addAll(buildAliasValidatorConfigs(anInterface1, context, checkFile));
			}

			checked.add(anInterface1.getName());
		}

		validatorConfigs.addAll(buildClassValidatorConfigs(clazz, checkFile));
		
		// 新增加代码
		String fileName = null;
		String method = ServletActionContext.getRequest().getMethod().toLowerCase();
		try {
			if (Class.forName("com.opensymphony.xwork2.ActionSupport") != clazz
					&& Class.forName("com.opensymphony.xwork2.ActionSupport").isAssignableFrom(clazz)) {
				// 到达我们所创建并继承 ActionSupport 的 Action 类
				// 拼装完整文件名并且载入文件,将载入的内容加入到验证配置中
				fileName = clazz.getName().replace('.', '/') + "-" + method + VALIDATION_CONFIG_SUFFIX;
				validatorConfigs.addAll(loadFile(fileName, clazz, checkFile));
				fileName = clazz.getName().replace('.', '/') + "-" + context.replace('/', '-') + "-" + method
						+ VALIDATION_CONFIG_SUFFIX;
				validatorConfigs.addAll(loadFile(fileName, clazz, checkFile));
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

		if (context != null) {
			validatorConfigs.addAll(buildAliasValidatorConfigs(clazz, context, checkFile));
		}

		checked.add(clazz.getName());

		return validatorConfigs;
	}



运行结果如下:

点击提交按钮后


实现了请求地址相同,但是能够通过不同的请求方式实现加载不同的验证 xml 文件

本文主要的目的在于弄清楚 Struts2 的源码,实现功能次要。

附上 Archive File 的下载地址。

链接:https://pan.baidu.com/s/1qXPivSC 密码:3bdr

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值