使用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.最 ...
随机推荐
- django的多对一,一对一,多对多关系
from django.db import models class Publisher(models.Model): name = models.CharField(max_length=30) a ...
- kafka ksql && docker 安装试用
备注: 使用docker 模式进行安装 1. 准备docker 环境(需要docker-compose) docker 的安装不需要进行多描述了,直接yum 或者源码编译也可以 ...
- jslinq 使用总结
最近一直在用 jslinq 感觉还是不错的.用于增强 Array.find() 上重点: 1: 引用 cnpm install jslinq --save (本人用淘宝镜像--npmFQ感觉也不快-- ...
- poj 3590 The shuffle Problem——DP+置换
题目:http://poj.org/problem?id=3590 bzoj 1025 的弱化版.大概一样的 dp . 输出方案的时候小的环靠前.不用担心 dp 时用 > 还是 >= 来转 ...
- bzoj 1119 [POI2009]SLO && bzoj 1697 [Usaco2007 Feb]Cow Sorting牛排序——思路(置换)
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1119 https://www.lydsy.com/JudgeOnline/problem.p ...
- ATM:模拟实现一个ATM + 购物商城程序
额度 15000或自定义 实现购物商城,买东西加入 购物车,调用信用卡接口结账 可以提现,手续费5% 支持多账户登录 支持账户间转账 记录每月日常消费流水 提供还款接口 ATM记录操作日志 提供管理接 ...
- class_create(),class_device_create()创建/dev/xxx 名字
在刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载 ...
- mysql流程控制
一 流程控制 delimiter // CREATE PROCEDURE proc_if () BEGIN declare i int default 0; if i = 1 THEN SELECT ...
- Python多线程-线程锁
多线程修改一份数据时需要用到线程锁,以防止数据修改出错 #-*- coding:utf-8 -*- __author__ = "MuT6 Sch01aR" import threa ...
- babel-preset-latest使用介绍
本文介绍babel中的babel-preset-latest插件 简介 该插件的作用是包含了每年的js代码转换规则 默认包含es2015,es2016,es2017,默认对这些代码都会进行转码,从而方 ...