Filter對Response的改變:HttpServletResponseWrapper的工作原理
Filter對Response的改變:HttpServletResponseWrapper的工作原理
馬克- to-win:馬克 java社區(qū):防盜版實名手機(jī)尾號: 73203。
馬克-to-win:前面我們講的知識,主要說的是由于Filter的參與,用戶的訪問路徑被改變的問題。底下我們就要講一點更難的話題,就是Filter 如何改變一個現(xiàn)有的html。比如我寫的新浪博客,寫完以后,一上傳,內(nèi)容有時有些改變,誰動的手腳?肯定是新浪公司編了什么Filter過濾器,把我的 html的內(nèi)容給改變了。馬克-to-win:現(xiàn)在問題是:這是如何實現(xiàn)的呢?這里核心問題其實就是如何改變Response?本來我的html在原來的 Response里,準(zhǔn)備返回給客戶端。但現(xiàn)在在Filter當(dāng)中被改變了。但這又是怎么改變的呢?這里涉及到一個 HttpServletResponseWrapper的類實例myWrapper問題。Wrapper英文就是包裹者的意思。正常情況下,我們過去的認(rèn)識是:chain.doFilter(request, response);的意思就是訪問完后面的目標(biāo)資源以后,目標(biāo)資源把要返回給客戶端的內(nèi)容放在Response當(dāng)中。而現(xiàn)在這里的例子就不同了:通過 chain.doFilter(request, myWrapper);目標(biāo)資源就會把要返回給客戶端的內(nèi)容放在myWrapper當(dāng)中了。這時在Filter當(dāng)中,我們就可以從myWrapper當(dāng)中取出返回給客戶端的內(nèi)容,接著就可以大大方方的對其進(jìn)行改變了。要想做成這件事兒,當(dāng)然還得符合sun公司制定的有關(guān)HttpServletResponseWrapper的所有規(guī)章制度。首先通過MarkToWinWrapper myWrapper = new MarkToWinWrapper((HttpServletResponse) response);讓response和myWrapper聯(lián)系起來。馬克-to-win:之后,在我編的MarkToWinWrapper這個普通類當(dāng)中,需要初始化一個CharArrayWriter的實例:myContent = new CharArrayWriter();和PrintWriter的實例pw=new PrintWriter(myContent);之后通過編寫public PrintWriter getWriter() { return pw; }。當(dāng)你執(zhí)行chain.doFilter(request, myWrapper);時,系統(tǒng)其中一步會調(diào)用getWriter(),得到pw以后,就會把你myWrapper構(gòu)造函數(shù)里得到的response和CharArrayWriter的實例:myContent聯(lián)系起來。最后當(dāng)你執(zhí)行 public String getResultMarkToWin() { return myContent.toString(); }時,返回給客戶端的內(nèi)容就被你得到了,因為response和myContent已經(jīng)被你聯(lián)系起來了。注意要想正確應(yīng)用 HttpServletResponseWrapper,必須遵守它的規(guī)則。下面的例子把AAA.html的“淘寶”倆字兒都變成了“百度”。
例 1.2.7
AAA.html
既然這是首頁,像淘寶首頁一樣,這底下是首頁的一些泛泛信息。
package com;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
public class MarkToWinFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
response.setCharacterEncoding("GBK");
PrintWriter out = response.getWriter();
MarkToWinWrapper myWrapper = new MarkToWinWrapper(
(HttpServletResponse) response);
/*下句話之后目標(biāo)jsp的東西都裝在myWrapper中了,不像Hello World的filter,那時都裝在response的out中,本例到目前為止,out是空的*/
chain.doFilter(request, myWrapper);
String result=myWrapper.getResultMarkToWin();
System.out.println("content : " +result );
result=result.replace("淘寶", "百度");
out.println("content : " + result);
}
public void init(FilterConfig fConfig) throws ServletException {
}
}
package com;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class MarkToWinWrapper extends HttpServletResponseWrapper {
private CharArrayWriter myContent;
private PrintWriter pw;
public MarkToWinWrapper(HttpServletResponse res) {
super(res);
myContent = new CharArrayWriter();
pw=new PrintWriter(myContent);
}
public PrintWriter getWriter() {
return pw;
}
public String getResultMarkToWin() {
return myContent.toString();
}
}
當(dāng)在瀏覽器中運行AAA.html時,console中輸出如下:
content :
既然這是首頁,像淘寶首頁一樣,這底下是首頁的一些泛泛信息。
而在瀏覽器中,輸出的是:
content :
既然這是首頁,像百度首頁一樣,這底下是首頁的一些泛泛信息。
7_a)HttpServletResponseWrapper對Servlet的過濾:
馬克-to- win:上面這個MarkToWinWrapper也可以過濾Servlet:現(xiàn)在的問題是,改變Servlet的response有什么用處?比如:現(xiàn)在你的Servlet從數(shù)據(jù)庫中獲取回數(shù)據(jù),放在response中,之后需要返回給客戶瀏覽器,返回前做敏感字濾除。馬克-to-win:如果你要在一百個Servlet當(dāng)中做這件事兒,即使你可以編寫一個普通類,然后每個Servlet都調(diào)用這個普通類的方法來轉(zhuǎn)化,這其實就造成了耦合。一旦有一天哪個普通類的類名,方法名或參數(shù)什么的需要改變,(這是經(jīng)常需要的,因為需求也在不斷變化)你的一百個Servlet里頭都需要改變。如果這件事要在 Filter當(dāng)中做,就不是這種場景了。只需改動Filter代碼即可,Servlet無需知道這件事兒。
例 1.2.7_a:
如果我們的Servlet代碼是:
package com;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletHello1 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
try {
PrintWriter pWriter=response.getWriter();
pWriter.println("淘寶淘寶淘寶淘寶");
} catch (IOException e) {
e.printStackTrace();
}
}
}
我們在瀏覽器中運行:http://localhost:8080/ServletHello/MarkToWinServlet
這時console中輸出如下:
content : 淘寶淘寶淘寶淘寶
瀏覽器中輸出如下:
content : 百度百度百度百度
總結(jié):我可以把Wrapper程序變成如下,也能工作:
package com;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class MarkToWinWrapper extends HttpServletResponseWrapper {
private CharArrayWriter myContent;
public MarkToWinWrapper(HttpServletResponse res) {
super(res);
}
public PrintWriter getWriter() {
myContent = new CharArrayWriter();
return new PrintWriter(myContent);
}
public String getResultMarkToWin() {
return myContent.toString();
}
}
或者下面的版本都可以:
package com;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class MarkToWinWrapper extends HttpServletResponseWrapper {
private CharArrayWriter myContent;
public MarkToWinWrapper(HttpServletResponse res) {
super(res);
myContent = new CharArrayWriter();
}
public PrintWriter getWriter() {
return new PrintWriter(myContent);
}
public String getResultMarkToWin() {
return myContent.toString();
}
}
原理:馬克-to-win:CharArrayWriter自帶buffer,開始為空,當(dāng)你把PrintWriter和CharArrayWriter連好后,chain.doFilter(request, myWrapper);內(nèi)部調(diào)用getWriter,內(nèi)部把response里的內(nèi)容都寫到CharArrayWriter的Buffer中, getResultMarkToWin需要你自己調(diào)用。myContent.toString()會返回buffer里的內(nèi)容。