Servlet规范中的Filter引入了一个功能强大的拦截模式。Filter能在request到达servlet的服务方法之前拦截request对象,而在服务方法转移控制后又能拦截response对象。

Tomcat 为了屏蔽内部的catalina容器的相关方法,使用户免受非sevlet标准方法的干扰。使用了两个包装类(RequestFacade 和 ResponseFacade)的实例传递给Servlet使用。这两个包装类分别实现了Servlet标准的(HttpServletRequest 和 HttpServletResponse)接口。关于其关系另起一文介绍:关于Tomcat中封装请求-响应的结构的分析

客户端发来的请求request对象中的参数是无法改变的,HttpServletRequest没有定义setter方法,无法进行修改操作。使用response输出的数据会写入到默认的输出端,所以无法获取到数据。


HttpServletRequestWrapper 和 HttpServletResponseWrapper

然而Servlet标准中的两个包装类(HttpServletRequestWrapper 和 HttpServletResponseWrapper)是(HttpServletRequest 和 HttpServletResponse)接口的实现类,它们的实例通过唯一的公开构造方法持有一个(HttpServletRequest 和 HttpServletResponse)的对象,并通过实现的(HttpServletRequest 和 HttpServletResponse)接口方法直接调用内部对象的对应方法,即该类包装了对请求和响应的所有接口操作。

通过继承这两个类,重写我们需要改变的代理方法得到我们需要的包装类,再将request和response对象包装到其实例中,将包装后的对象通过调用链传递下去即可实现某些特殊操作。例如:

继承HttpServletRequestWrapper类并重写部分getter方法,达到“修改”请求参数的需求(代理对象中持有的原对象并未改变,只是通过代理对象中重写的getter方法返回设置的值)。

继承HttpServletResponseWrapper类并重写getWriter()方法,拦截后续响应数据,使之不通过原对象直接输出到客户端,便于处理数据。


实例

一个拦截response响应数据的例子:

首先编写了一个过滤器过滤指定url-pattern上的Servlet:

Filter.java的doFilter方法:

	/**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// TODO Auto-generated method stub
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
// place your code here
SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd HH:mm:ss.SSS");
response.getWriter().append(sdf.format(new Date()) + "过滤器LogFilter前半段被执行<br />");
// pass the request along the filter chain
chain.doFilter(request, response);
response.getWriter().append("<br />" + sdf.format(new Date()) + "过滤器LogFilter后半段被执行"); }

然后新建一个包装类继承于HttpServletResponseWrapper,编写构造函数传入HttpServletResponse参数并直接传给父类构造方法,设置一个用来存放拦截到的响应数据的CharArrayWriter对象,重写getWriter()方法将CharArrayWriter对象返回,从而拦截调用getWriter()方法输出的响应数据到CharArrayWriter对象中,再设置一个获取CharArrayWriter对象的方法用于读取拦截到的响应数据:

<代理类>.java

public class CheckResponseData extends HttpServletResponseWrapper {

	private CharArrayWriter charArrayWriter = new CharArrayWriter();

	public CheckResponseData(HttpServletResponse response) {
super(response);
// TODO 自动生成的构造函数存根
} @Override
public PrintWriter getWriter() throws IOException {
return new PrintWriter(charArrayWriter);
} public CharArrayWriter getCharWriter() {
return charArrayWriter;
} }

修改之前的过滤器的doFilter方法为:

	/**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// TODO Auto-generated method stub
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
// place your code here
SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd HH:mm:ss.SSS");
response.getWriter().append(sdf.format(new Date()) + "过滤器LogFilter前半段被执行<br />");
// pass the request along the filter chain
System.out.println("构造响应数据拦截对象");
CheckResponseData checkResponseData = new CheckResponseData((HttpServletResponse) response);
checkResponseData.setCharacterEncoding("utf-8");
System.out.println("开始调用链");
chain.doFilter(request, checkResponseData);
System.out.println("调用链返回");
response.getWriter().append("<br />" + sdf.format(new Date()) + "过滤器LogFilter后半段被执行");
String realRes = checkResponseData.getCharWriter().toString();
System.out.println(realRes);
response.getWriter().append("<br />Filter通过代理对象截获了自调用链开始到返回时的响应数据:<br />"+realRes+"<br />#到此结束#<br />");
/*
chain.doFilter(request, response);
response.getWriter().append("<br />" + sdf.format(new Date()) + "过滤器LogFilter后半段被执行");
*/ }

这样在过滤器被执行时会向后续调用链传递一个包装了原响应对象的代理对象,通过重写的getWriter()代理方法拦截了后续响应数据到代理对象中的CharArrayWriter对象里,在调用链返回该过滤器时再将拦截到的数据放在两行标识之间输出,效果如下:

18-01-18 02:51:50.963过滤器LogFilter前半段被执行

18-01-18 02:51:50.976过滤器LogFilter后半段被执行
Filter通过代理对象截获了自调用链开始到返回时的响应数据:
Servlet被执行
#即将使用include转发 HTTP请求实例 accept:application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* accept-language:zh-CN cache-control:no-cache ua-cpu:*********** accept-encoding:gzip, deflate user-agent:************************** host:localhost:8080 connection:Keep-Alive Servlet继续执行
#到此结束#

成功拦截到了后续响应数据。


更多用法参照上例,基本思路就是通过代理对象中重写的代理方法改变原请求-响应流程。

使用HttpServletRequestWrapper修改请求参数 和 使用HttpServletResponseWrapper截获响应数据的更多相关文章

  1. fidder设置断点,修改请求参数等

    设置断点(来自:http://jingyan.baidu.com/article/17bd8e52216c8d85ab2bb8e9.html): 可以看到当前有一个抓取的很多的包的链接的地址的信息,那 ...

  2. charles 模拟手机弱网、修改请求参数、修改返回值

    1.charles模拟弱网(断网) 2.charles修改请求参数 (1)先访问一次需要改的请求,在charles上找到相应的请求地址 (2)然后在需要打断点的请求上右键,勾选[Breakpoints ...

  3. fiddler修改请求参数

    1.打开fiddler ,点击界面左侧左侧底部 2.此图标为before request请求(修改请求参数时,设置这个,可以修改请求参数) 3..再次点击该按钮,将图标切换到下图after respo ...

  4. charles技能之修改请求参数/返回数据(map Local、Rewrite、Breakpoints)

    之前一直用postman调接口比较多,但有时候想要去修改APP的页面展示,造数据又会比较麻烦,此时可以用以下三种方法修改请求参数或修改响应: map Local(本地映射).Breakpoints(打 ...

  5. Spring Cloud Gateway 动态修改请求参数解决 # URL 编码错误传参问题

    Spring Cloud Gateway 动态修改请求参数解决 # URL 编码错误传参问题 继实现动态修改请求 Body 以及重试带 Body 的请求之后,我们又遇到了一个小问题.最近很多接口,收到 ...

  6. 通过zuul修改请求参数——对请求参数进行解密

    zuul是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用,Zuul 在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架,Zuul 相当于是设备和 ...

  7. Java Web 修改请求参数

    方法一.继承 HttpServletRequestWrapper , 实现自定义 request 1.除了修改的参数,其他 Header 等参数不变, 等同于修改了请求参数 2.实质是另一个请求 /* ...

  8. charles抓包修改请求参数发送新的请求

    打开charles -->选择请求右击选择compose---修改参数发送请求

  9. charles功能(二)修改response请求参数

    1.接口处 鼠标右击,选择breakpoints(允许本接口使用breakpionts功能) 2.开始设置断点值 4.重新请求接口(charles的界面变为可编辑状态),修改请求参数,执行请求 5.最 ...

随机推荐

  1. noip济南清北冲刺班DAY2

    题解: 贪心+dp 30% N<=5  5!枚举一下 20%  高度没有的时候,高度花费就不存在了,将ci排序, 从小到大挨个跳.另外,20% 准备跳楼没有花费,那么跳 楼的高度一定是从小到大, ...

  2. php通过存储过程传入汉字参数并写入数据库

    写入数据库,汉字为???样式的乱码,后根据网上介绍的方法,参数前加N,数据库汉字内容变成空白. 解决方法,在PHP中先转为base64,再在mysql中base64解码,前提先保证mysql中有bas ...

  3. Oracle事务的隔离

    事务是指一些列操作的集合,它有4个属性:原子性(Automacity).一致性(Consistency).隔离性(Isolation)和持久性(Durability),这4个属性简称为ACID.原子性 ...

  4. Linux bash shell 入门

    https://www.cnblogs.com/cosiray/archive/2012/03/02/2377099.html

  5. jave获取音频时长

    本文转载自:http://blog.csdn.net/ntotl/article/details/50419983 下载 jave-1.0.2.jar File source =new File('d ...

  6. 装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况

     装饰器分成三种: 1.无参数的: 2.有参数的: 3.装饰器本身带参数的. 装饰器decorator又叫语法糖 定义:本质是函数,器就是函数的意思.装饰其他函数.就是为其他函数添加附加功能. 原则: ...

  7. java面试(6)

    1  六大原则 详情参考:设计模式六大原则(转载). 2  UML类之间关系有几种?聚合和组合区别? 类之间可能存在以下几种关系:关联(association).依赖(dependency).聚合(A ...

  8. 消息队列函数(msgget、msgctl、msgsnd、msgrcv)及其范例

    消息队列函数由msgget.msgctl.msgsnd.msgrcv四个函数组成.下面的表格列出了这四个函数的函数原型及其具体说明. 1.   msgget函数原型 msgget(得到消息队列标识符或 ...

  9. Oracle 复杂查询(1)

    一.复杂查询 1. 列出至少有一个员工的所有部门编号.名称,并统计出这些部门的平均工资.最低工资.最高工资. 1.确定所需要的数据表: emp表:可以查询出员工的数量: dept表:部门名称: emp ...

  10. Flask之数据库迁徙

    4.3 数据库迁移 在开发过程中,需要修改数据库模型,而且还要在修改之后更新数据库.最直接的方式就是删除旧表,但这样会丢失数据. 更好的解决办法是使用数据库迁移框架,它可以追踪数据库模式的变化,然后把 ...