继承HttpServletRequestWrapper以实现在Filter中修改HttpServletRequest的参数

一 简介

如题所示,有时候我们需要在一个请求到达Controller之前能够截获其请求,并且根据其具体情况对 HttpServletRequest 中的参数进行过滤或者修改。这时,有的同学可能会想:我们是否可以在一个Filter中将 HttpServletRequest 里的所有参数都取出来分别进行过滤然后再放回到该HttpServletRequest 中呢?

很显然,在 HttpServletRequest 貌似只有 setAttribute(String name, Object o) 这个方法可以设置参数,但是我们经过尝试之后可以发现:使用 setAttribute(String name, Object o) 方法来重新设置参数显然是不行的,因为在Controller中获取参数本质上还是调用的ServletRequest的public String getParameter(String name) 或者 public String[] getParameterValues(String name) 方法,因此想要达到“在Filter中修改HttpServletRequest的参数”的目的,显然是需要使用装饰模式来复写这些方法才行的

在正式代码之前,我还是先简单介绍下ServletRequest、HttpServletRequest、ServletRequestWrapper以及HttpServletRequestWrapper这几个接口或者类之间的层次关系,并说明“继承HttpServletRequestWrapper类以实现在Filter中修改HttpServletRequest的参数”这种方式的原理是什么

如果我们从网上下载tomcat的源代码并查看的话,就可以很清楚地看到这几个类之间的层次关系了,在eclipse中看,它们之间的层次关系是这样的:

如果这个图表还不够清楚地话,我还画了一个简单的UML结构图:

注:因为我现在没有下载专门的UML建模工具,因此就使用“画图”工具简单地画了一下类图,同时这里的ModifyParametersWrapper 是我后面举例用到的的一个自定义的类

如果学过“装饰模式”的童鞋可能已经发现了,上面这个关系毫无疑问是一个很标准的装饰模式:

  • ServletRequest    抽象组件
  • HttpServletRequest    抽象组件的一个子类,它的实例被称作“被装饰者”
  • ServletRequestWrapper    一个基本的装饰类,这里是非抽象的
  • HttpServletRequestWrapper    一个具体的装饰者,当然这里也继承了HttpServletRequest这个接口,是为了获取一些在ServletRequest中没有的方法
  • ModifyParametersWrapper    同样是 一个具体的装饰者(PS:我自定义的一个类)

注:一个标准的装饰模式的UML类图是这样的:

那么问题来了,如何在Filter中修改后台Controller中获取到的HttpServletRequest中的参数?

答:很简单,只需要在Filter中自定义一个类继承于HttpServletRequestWrapper,并复写getParameterNames、getParameter、getParameterValues等方法即可

二 代码实现

(1)自定义的过滤器ModifyParametersFilter.java:

package cn.zifangsky.filter;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Vector;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.filter.OncePerRequestFilter;

public class ModifyParametersFilter extends OncePerRequestFilter {

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		ModifyParametersWrapper mParametersWrapper = new ModifyParametersWrapper(request);
		filterChain.doFilter(mParametersWrapper, response);
	}

	/**
	 * 继承HttpServletRequestWrapper,创建装饰类,以达到修改HttpServletRequest参数的目的
	 */
	private class ModifyParametersWrapper extends HttpServletRequestWrapper {
		private Map<String, String[]> parameterMap; // 所有参数的Map集合

		public ModifyParametersWrapper(HttpServletRequest request) {
			super(request);
			parameterMap = request.getParameterMap();
		}

		// 重写几个HttpServletRequestWrapper中的方法
		/**
		 * 获取所有参数名
		 * 
		 * @return 返回所有参数名
		 */
		@Override
		public Enumeration<String> getParameterNames() {
			Vector<String> vector = new Vector<String>(parameterMap.keySet());
			return vector.elements();
		}

		/**
		 * 获取指定参数名的值,如果有重复的参数名,则返回第一个的值 接收一般变量 ,如text类型
		 * 
		 * @param name
		 *            指定参数名
		 * @return 指定参数名的值
		 */
		@Override
		public String getParameter(String name) {
			String[] results = parameterMap.get(name);
			if (results == null || results.length <= 0)
				return null;
			else {
				System.out.println("修改之前: " + results[0]);
				return modify(results[0]);
			}
		}

		/**
		 * 获取指定参数名的所有值的数组,如:checkbox的所有数据 
		 * 接收数组变量 ,如checkobx类型
		 */
		@Override
		public String[] getParameterValues(String name) {
			String[] results = parameterMap.get(name);
			if (results == null || results.length <= 0)
				return null;
			else {
				int length = results.length;
				for (int i = 0; i < length; i++) {
					System.out.println("修改之前2: " + results[i]);
					results[i] = modify(results[i]);
				}
				return results;
			}
		}

		/**
		 * 自定义的一个简单修改原参数的方法,即:给原来的参数值前面添加了一个修改标志的字符串
		 * 
		 * @param string
		 *            原参数值
		 * @return 修改之后的值
		 */
		private String modify(String string) {
			return "Modified: " + string;
		}
	}

}

上面的代码很简单,就是添加了一个内部类:ModifyParametersWrapper,然后复写了ServletRequest中的几个方法,具体来说就是将原来的每个参数的值的前面加上了“Modified: ”这个字符串

(2)在web.xml中注册该过滤器:

	<filter>
		<filter-name>ModifyParametersFilter</filter-name>
		<filter-class>cn.zifangsky.filter.ModifyParametersFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>ModifyParametersFilter</filter-name>
		<url-pattern>/param/*</url-pattern>
		<!-- 直接从客户端过来的请求以及通过forward过来的请求都要经过该过滤器 -->
		<dispatcher>REQUEST</dispatcher>
		<dispatcher>FORWARD</dispatcher>
	</filter-mapping>

(3)添加一个测试使用的Controller,即:TestModifyController.java:

package cn.zifangsky.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class TestModifyController {

	@RequestMapping("/param/modify.html")
	public void modify(@RequestParam("name") String name){
		System.out.println("修改之后: " + name);
	}
}

这里没有处理复杂的逻辑,仅仅只是简单地输出

(4)测试:

启动项目之后访问:http://localhost:9180/FilterDemo/param/modify.html?name=abc

可以发现,在控制台中输出如下:

  修改之前2: abc
修改之后: Modified: abc

这就表明了我们前面自定义的过滤器已经将HttpServletRequest中原来的参数成功修改了。同时,还说明SpringMVC的@RequestParam注解本质上调用的是ServletRequest中的 getParameterValues(String name) 方法而不是 getParameter(String name) 方法

注:tomcat-8.5.5源码下载地址:http://pan.baidu.com/s/1skWOso9

参考文章:

PS:上面图片中的水印是我个人博客的域名,因此还请管理员手下留情不要给我标为“转载文章”,谢谢!!!

时间: 2025-01-07 05:47:10

继承HttpServletRequestWrapper以实现在Filter中修改HttpServletRequest的参数的相关文章

如何在Java Filter 中注入 Service

在项目中遇到一个问题,在 Filter中注入 Serivce失败,注入的service始终为null.如下所示: public class WeiXinFilter implements Filter{ @Autowired private UsersService usersService; public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOExc

解决在Filter中读取Request中的流后,后续controller或restful接口中无法获取流的问题

首先我们来描述一下在开发中遇到的问题,场景如下: 比如我们要拦截所有请求,获取请求中的某个参数,进行相应的逻辑处理:比如我要获取所有请求中的公共参数 token,clientVersion等等:这个时候我们通常有两种做法 前提条件是我们实现Filter类,重写doFilter方法 1.通过getParameter方法获得 HttpServletRequest hreq = (HttpServletRequest) req; String param = hreq.getParameter("pa

java web中get请求中文乱码在filter中解决

之前已经讲过get或者post方法的中文乱码问题,之前都是在每个方法中编写设置编码.如果程序变大,就会很繁琐,使用filter可以避免这种繁琐. 1)写一个encodingFilter进行编码设置 public class encodingFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain arg2) throws IOExce

在Java filter中调用service层方法

在项目中遇到一个问题,在 Filter中注入 Serivce失败,注入的service始终为null.如下所示: 1 public class WeiXinFilter implements Filter{ 2 3 @Autowired 4 private UsersService usersService; 5 6 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

spring拦截器中修改响应消息头

问题描述 前后端分离的项目,前端使用Vue,后端使用Spring MVC. 显然,需要解决浏览器跨域访问数据限制的问题,在此使用CROS协议解决. 由于该项目我在中期加入的,主要负责集成shiro框架到项目中作为权限管理组件,之前别的同事已经写好了部分接口,我负责写一部分新的接口. 之前同事解决跨域问题使用Spring提供的@CrossOrigin注解: @RequestMapping(value = "/list.do", method = RequestMethod.GET) @R

Android下通过root实现对system_server中binder的ioctl调用拦截

Android下通过root实现对system_server中binder的ioctl调用拦截 分类: Android2013-06-19 18:09 779人阅读 评论(0) 收藏 举报 作 者: Passion时 间: 2012-10-18,13:53:53链 接: http://bbs.pediy.com/showthread.php?t=157419 Android下通过root实现对system_server中binder的ioctl调用拦截作者:passion2012-10-18关键

解决在Filter中读取Request中的流后, 然后再Control中读取不到的做法

我们来看一下核心代码: filter中主要做的事情, 就是来校验请求是否合法, 是否有篡改过值. @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (Boolean.valueOf(authentication)) { HttpServletRequest http

directshow filter中添加属性页

directShow 属性页的制作,为CBall filter加了一个属性页 具体为分以下步骤: 1.在要显示属性的类中继承现ISpecifyPropertyPages类,并实现此类的GetPages()函数. 如下所示:HRESULT STDMETHODCALLTYPE CBouncingBall::GetPages(CAUUID *pPages){  if (pPages == NULL) return E_POINTER;  pPages->cElems = 1;  pPages->pE

如何实现在PHP中调用JAVA

详细说明:http://php.662p.com/thread-275-1-1.html PHP与JAVA JAVA是个非常强大的编程利器,它的扩展库也是非常的有用,这篇教程,主要讲述怎样使用PHP调用功能强大的JAVA 类库(classes).为了方便你的学习,这篇教程将包括JAVA的安装及一些基本的例子. windows下的安装 第一步:安装JDK,这是非常容易的,你只需一路回车的安装好.然后做好以下步骤. 在 Win9x 下加入 :“PATH=%PATH%;C:\jdk1.2.2\bin”