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. 实体对象,List泛型 转换为DataTable

    /// <summary>        /// 实体对象转换DataTable        /// </summary>        /// <param name ...

  2. iOS 10 隐私权限设置

    iOS 10 开始对隐私权限更加严格,如果你不设置就会直接崩溃,现在很多遇到崩溃问题了,一般解决办法都是在info.plist文件添加对应的Key-Value就可以了. 以上Value值,圈出的红线部 ...

  3. 从如何优化SQL入手,提高数据仓库的ETL效率

    1        引言数据仓库建设中的ETL(Extract, Transform, Load)是数据抽取.转换和装载到模型的过程,整个过程基本是通过控制用SQL语句编写的存储过程和函数的方式来实现对 ...

  4. C# 实现程序只启动一次(多次运行激活第一个实例,使其获得焦点,并在最前端显示)

    防止程序运行多个实例的方法有多种,如:通过使用互斥量和进程名等.而我想要实现的是:在程序运行多个实例时激活的是第一个实例,使其获得焦点,并在前端显示. 主要用到两个API 函数: ShowWindow ...

  5. PADS Logic 脚本的 Fields 一个对象记录

    PADS Logic 脚本的 Fields 一个对象记录 PADS Laogic 有一个非常棒的脚本功能,可以导出所以元件. 我目前是把脚本绑定到 Ctrl+S 上,在保存时自动导出 txt 文件,方 ...

  6. eclipse解决git冲突举例

    本地修改了两个文件,提交时提示有冲突,想来应该是没有从远程仓库下载最新代码导致的.通过右击项目 -> Team -> Sychronized WorkSpace,比较本地仓库和远程仓库的异 ...

  7. Tomcat起了一个测试桩,调用该测试桩无响应

    有时在测试新业务流程时因为涉及多个不同接口的调用,而这些被调用的服务端因为网络权限或开发进度问题暂时对我们不可达,那么我们可以通过模拟接口返回来完成我们新业务的测试.这次碰到的问题是我明明起了该测试桩 ...

  8. 蓝桥杯 算法训练 ALGO-93 反置数

    算法训练 反置数   时间限制:1.0s   内存限制:512.0MB 问题描述 一个整数的“反置数”指的是把该整数的每一位数字的顺序颠倒过来所得到的另一个整数.如果一个整数的末尾是以0结尾,那么在它 ...

  9. 转: SQL中的where条件,在数据库中提取与应用浅析

    SQL中的where条件,在数据库中提取与应用浅析 http://hedengcheng.com/?p=577 1问题描述 一条SQL,在数据库中是如何执行的呢?相信很多人都会对这个问题比较感兴趣.当 ...

  10. JS比较实用的时间控件

    使用方法: 下载下来压缩包,文件的地方不要改变,就可以了 http://www.my97.net/dp/down.asp html的代码: <input readonly="reado ...