zoukankan      html  css  js  c++  java
  • 使用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继续执行
    #到此结束#
    

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


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

  • 相关阅读:
    【数据结构】算法 Invert Binary Tree 翻转二叉树
    雪碧图
    闭包
    正则那些事
    JS添加,删除表格中的行那些事
    三目运算
    10个不能重复的随机数要求为55-109的整数, 要将10个随机数打印出来,并且将10个随机数里面能够被5整除的数打印出来,最后将能够被5整除的数叠加的结果打印出来
    输出从小到大排序好的五个不重复的随机整数,范围[10-23)。努力的人全世界都为你让路!你的努力终将美好!
    随机取10个在55-80之间的数,按照从小到大排序输出,冒泡排序
    求10个随机数,随机数要求为25-99的整数.能够被3整除的数
  • 原文地址:https://www.cnblogs.com/Leroscox/p/8307813.html
Copyright © 2011-2022 走看看