使用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繼續執行
#到此結束#

 成功攔截到了后續響應數據。


更多用法參照上例,基本思路就是通過代理對象中重寫的代理方法改變原請求-響應流程。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM