Filter對(duì)Response的改變:HttpServletResponseWrapper的工作原理

Filter對(duì)Response的改變:HttpServletResponseWrapper的工作原理
馬克- to-win:馬克 java社區(qū):防盜版實(shí)名手機(jī)尾號(hào): 73203。
馬克-to-win:前面我們講的知識(shí),主要說(shuō)的是由于Filter的參與,用戶的訪問(wèn)路徑被改變的問(wèn)題。底下我們就要講一點(diǎn)更難的話題,就是Filter 如何改變一個(gè)現(xiàn)有的html。比如我寫的新浪博客,寫完以后,一上傳,內(nèi)容有時(shí)有些改變,誰(shuí)動(dòng)的手腳?肯定是新浪公司編了什么Filter過(guò)濾器,把我的 html的內(nèi)容給改變了。馬克-to-win:現(xiàn)在問(wèn)題是:這是如何實(shí)現(xiàn)的呢?這里核心問(wèn)題其實(shí)就是如何改變Response?本來(lái)我的html在原來(lái)的 Response里,準(zhǔn)備返回給客戶端。但現(xiàn)在在Filter當(dāng)中被改變了。但這又是怎么改變的呢?這里涉及到一個(gè) HttpServletResponseWrapper的類實(shí)例myWrapper問(wèn)題。Wrapper英文就是包裹者的意思。正常情況下,我們過(guò)去的認(rèn)識(shí)是:chain.doFilter(request, response);的意思就是訪問(wèn)完后面的目標(biāo)資源以后,目標(biāo)資源把要返回給客戶端的內(nèi)容放在Response當(dāng)中。而現(xiàn)在這里的例子就不同了:通過(guò) chain.doFilter(request, myWrapper);目標(biāo)資源就會(huì)把要返回給客戶端的內(nèi)容放在myWrapper當(dāng)中了。這時(shí)在Filter當(dāng)中,我們就可以從myWrapper當(dāng)中取出返回給客戶端的內(nèi)容,接著就可以大大方方的對(duì)其進(jìn)行改變了。要想做成這件事兒,當(dāng)然還得符合sun公司制定的有關(guān)HttpServletResponseWrapper的所有規(guī)章制度。首先通過(guò)MarkToWinWrapper myWrapper = new MarkToWinWrapper((HttpServletResponse) response);讓response和myWrapper聯(lián)系起來(lái)。馬克-to-win:之后,在我編的MarkToWinWrapper這個(gè)普通類當(dāng)中,需要初始化一個(gè)CharArrayWriter的實(shí)例:myContent = new CharArrayWriter();和PrintWriter的實(shí)例pw=new PrintWriter(myContent);之后通過(guò)編寫public PrintWriter getWriter() { return pw; }。當(dāng)你執(zhí)行chain.doFilter(request, myWrapper);時(shí),系統(tǒng)其中一步會(huì)調(diào)用getWriter(),得到pw以后,就會(huì)把你myWrapper構(gòu)造函數(shù)里得到的response和CharArrayWriter的實(shí)例:myContent聯(lián)系起來(lái)。最后當(dāng)你執(zhí)行 public String getResultMarkToWin() { return myContent.toString(); }時(shí),返回給客戶端的內(nèi)容就被你得到了,因?yàn)閞esponse和myContent已經(jīng)被你聯(lián)系起來(lái)了。注意要想正確應(yīng)用 HttpServletResponseWrapper,必須遵守它的規(guī)則。下面的例子把AAA.html的“淘寶”倆字兒都變成了“百度”。

例 1.2.7

AAA.html


既然這是首頁(yè),像淘寶首頁(yè)一樣,這底下是首頁(yè)的一些泛泛信息。





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,那時(shí)都裝在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)在瀏覽器中運(yùn)行AAA.html時(shí),console中輸出如下:


content :

既然這是首頁(yè),像淘寶首頁(yè)一樣,這底下是首頁(yè)的一些泛泛信息。




而在瀏覽器中,輸出的是:

content :

既然這是首頁(yè),像百度首頁(yè)一樣,這底下是首頁(yè)的一些泛泛信息。



7_a)HttpServletResponseWrapper對(duì)Servlet的過(guò)濾:


馬克-to- win:上面這個(gè)MarkToWinWrapper也可以過(guò)濾Servlet:現(xiàn)在的問(wèn)題是,改變Servlet的response有什么用處?比如:現(xiàn)在你的Servlet從數(shù)據(jù)庫(kù)中獲取回?cái)?shù)據(jù),放在response中,之后需要返回給客戶瀏覽器,返回前做敏感字濾除。馬克-to-win:如果你要在一百個(gè)Servlet當(dāng)中做這件事兒,即使你可以編寫一個(gè)普通類,然后每個(gè)Servlet都調(diào)用這個(gè)普通類的方法來(lái)轉(zhuǎn)化,這其實(shí)就造成了耦合。一旦有一天哪個(gè)普通類的類名,方法名或參數(shù)什么的需要改變,(這是經(jīng)常需要的,因?yàn)樾枨笠苍诓粩嘧兓┠愕囊话賯€(gè)Servlet里頭都需要改變。如果這件事要在 Filter當(dāng)中做,就不是這種場(chǎng)景了。只需改動(dòng)Filter代碼即可,Servlet無(wú)需知道這件事兒。




例 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();
        }        
    }
}


我們?cè)跒g覽器中運(yùn)行:http://localhost:8080/ServletHello/MarkToWinServlet

這時(shí)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,開(kāi)始為空,當(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()會(huì)返回buffer里的內(nèi)容。