使用HttpServletRequestWrapper修改请求参数 和 使用HttpServletResponseWrapper截获响应数据
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截获响应数据的更多相关文章
- fidder设置断点,修改请求参数等
设置断点(来自:http://jingyan.baidu.com/article/17bd8e52216c8d85ab2bb8e9.html): 可以看到当前有一个抓取的很多的包的链接的地址的信息,那 ...
- charles 模拟手机弱网、修改请求参数、修改返回值
1.charles模拟弱网(断网) 2.charles修改请求参数 (1)先访问一次需要改的请求,在charles上找到相应的请求地址 (2)然后在需要打断点的请求上右键,勾选[Breakpoints ...
- fiddler修改请求参数
1.打开fiddler ,点击界面左侧左侧底部 2.此图标为before request请求(修改请求参数时,设置这个,可以修改请求参数) 3..再次点击该按钮,将图标切换到下图after respo ...
- charles技能之修改请求参数/返回数据(map Local、Rewrite、Breakpoints)
之前一直用postman调接口比较多,但有时候想要去修改APP的页面展示,造数据又会比较麻烦,此时可以用以下三种方法修改请求参数或修改响应: map Local(本地映射).Breakpoints(打 ...
- Spring Cloud Gateway 动态修改请求参数解决 # URL 编码错误传参问题
Spring Cloud Gateway 动态修改请求参数解决 # URL 编码错误传参问题 继实现动态修改请求 Body 以及重试带 Body 的请求之后,我们又遇到了一个小问题.最近很多接口,收到 ...
- 通过zuul修改请求参数——对请求参数进行解密
zuul是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用,Zuul 在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架,Zuul 相当于是设备和 ...
- Java Web 修改请求参数
方法一.继承 HttpServletRequestWrapper , 实现自定义 request 1.除了修改的参数,其他 Header 等参数不变, 等同于修改了请求参数 2.实质是另一个请求 /* ...
- charles抓包修改请求参数发送新的请求
打开charles -->选择请求右击选择compose---修改参数发送请求
- charles功能(二)修改response请求参数
1.接口处 鼠标右击,选择breakpoints(允许本接口使用breakpionts功能) 2.开始设置断点值 4.重新请求接口(charles的界面变为可编辑状态),修改请求参数,执行请求 5.最 ...
随机推荐
- verilog 2001中的一些新语法
比较有用的:1,generate语句,但需注意,generate-for中变量范围是已知的确定值, generate-case,generate-if语句中变量都必须是固定的, generate必须跟 ...
- linux centos6.5 安装gcc-c++时出现 yum install gcc-c++ cannot find a valid baseurl for repo...
1.输入命令:cd /etc/sysconfig/network-scripts/ 2.ls查看该文件夹下 3.vi ifcfg-eth0 按i进行编辑,添加如下两行后,esc →shift+:→wq ...
- jquery选择器 之 获取父级元素,子元素,同级元素
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <link href ...
- ①SpringBoot入门教学篇
一.什么是spring boot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发 ...
- 关于bonecp和QuerRunner
之前一直以为boneCP和QueryRunner是绑定的,但是其实不是,后者来自于commons-dbUtils,BoneCP就是负责连接池. while preparing SQL: UPSERT ...
- 有关implicit Intent的使用
Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse("geo:20.000,50.000&quo ...
- gen_server模块
转自http://www.xuebuyuan.com/2132233.html
- Quartz.net 2.x 学习笔记02-Quartz.net 2.x在MVC站点中结合Log4net的使用
Quartz.net 2.x在MVC站点中结合Log4net的使用 首先新建一个MVC的空站点: 第二步,添加Quartz.net的引用 在搜索处输入quartz.net搜索安装即可(目前是2.3) ...
- 微信小程序之如何使用iconfont
如何在小程序中使用iconfont 1.添加入库 2.加入项目 3.下载ttf 4.进行base64处理,在这个平台https://transfonter.org/ 上转换一下格式为base64位. ...
- from表单
构建一个表单 假设你想在你的网站上创建一个简单的表单,以获得用户的名字.你需要类似这样的模板: 1 2 3 4 5 <form action="/your-name/" me ...