Servlet過濾器與封裝器

在Servlet容器調(diào)用某個(gè)Servlet的service()方法前,Servlet并不會(huì)知道有請(qǐng)求的到來其垄,而在Servlet的service()方法運(yùn)行之后乳讥,容器真正對(duì)瀏覽器進(jìn)行HTTP響應(yīng)之前,瀏覽器也不會(huì)知道Servlet真正的響應(yīng)是什么祸憋。過濾器正如其名稱所示吐咳,它介于Servlet之前逻悠,可攔截過濾瀏覽器對(duì)Servlet的請(qǐng)求,也可以改變Servlet對(duì)瀏覽器的響應(yīng)韭脊。本文將介紹過濾器的運(yùn)用童谒,了解如何實(shí)現(xiàn)Filter接口來編寫過濾器,以及如何使用請(qǐng)求封裝器及響應(yīng)封裝器沪羔,將容器產(chǎn)生的請(qǐng)求與響應(yīng)對(duì)象加以包裝饥伊,針對(duì)某些請(qǐng)求信息或響應(yīng)進(jìn)行加工處理。

1、過濾器的概念

想象已經(jīng)開發(fā)好應(yīng)用程序的主要商務(wù)功能了琅豆,但現(xiàn)在有幾個(gè)需求出現(xiàn):

(1)針對(duì)所有的servlet愉豺,產(chǎn)品經(jīng)理想要了解從請(qǐng)求到響應(yīng)之間的時(shí)間差。
  (2)針對(duì)某些特定的頁面茫因,客戶希望只有特定的幾個(gè)用戶有權(quán)瀏覽粒氧。
  (3)基于安全的考量,用戶輸入的特定字符必須過濾并替換為無害的字符节腐。
  (4)請(qǐng)求與響應(yīng)的編碼從Big5改用UTF-8。

在修改源代碼之前摘盆,先分析一下這些需求:

(1)在運(yùn)行Servlet的service()方法“前”翼雀,記錄起始時(shí)間,Servlet的service()方法運(yùn)行“后”孩擂,記錄結(jié)束時(shí)間并計(jì)算時(shí)間差狼渊。
  (2)在運(yùn)行Servlet的service()方法“前”,驗(yàn)證是否為允許的用戶类垦。
  (3)在運(yùn)行Servlet的service()方法“前”狈邑,對(duì)請(qǐng)求參數(shù)進(jìn)行字符過濾與替換。
  (4)在運(yùn)行Servlet的service()方法“前”蚤认,對(duì)請(qǐng)求與響應(yīng)對(duì)象設(shè)置編碼米苹。

經(jīng)過以上分析,可以發(fā)現(xiàn)這些需求砰琢,可以在真正運(yùn)行Servlet的service方法“前”與Servlet的service()方法“后”中間進(jìn)行實(shí)現(xiàn)蘸嘶。如下圖所示:


image.png

性能評(píng)測(cè)、用戶驗(yàn)證陪汽、字符替換训唱、編碼設(shè)置等需求,基本上與應(yīng)用程序的業(yè)務(wù)邏輯沒有直接的關(guān)系挚冤,只是應(yīng)用程序額外的元件服務(wù)之一况增。因此,這些需求應(yīng)該設(shè)計(jì)為獨(dú)立的元件训挡,使之隨時(shí)可以加入到應(yīng)用程序中澳骤,也隨時(shí)可以移除,或隨時(shí)可以修改設(shè)置而不用修改原有的業(yè)務(wù)代碼舍哄。這類元件就像是一個(gè)過濾器宴凉,安插在瀏覽器與Servlet中間,可以過濾請(qǐng)求與響應(yīng)而作進(jìn)一步的處理表悬,如下圖所示弥锄。


image.png

Servlet/JSP提供了過濾器機(jī)制讓你實(shí)現(xiàn)這些元件服務(wù),可以視 需求抽換過濾器或調(diào)整過濾器的順序,也可以針對(duì)不同的URL應(yīng)用不同的過濾器籽暇。甚至在不同的Servlet間請(qǐng)求轉(zhuǎn)發(fā)或包含時(shí)應(yīng)用過濾器温治。

2、實(shí)現(xiàn)并設(shè)置過濾器

在Servlet中要實(shí)現(xiàn)過濾器戒悠,必須實(shí)現(xiàn)Filter接口熬荆,并使用@WebFilter標(biāo)注或在web.xml中定義過濾器,讓容器知道該加載哪些過濾器類绸狐。Filter接口有三個(gè)要實(shí)現(xiàn)的方法:init()卤恳、doFilter()與destroy()。

package javax.servlet;
import java.io.IOException;

public interface Filter {

    public void init(FilterConfig filterConfig) throws ServletException;
    public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException;
    public void destroy();

}

FilterConfig類似于Servlet接口init()方法參數(shù)上的ServletConfig寒矿,F(xiàn)ilterConfig是實(shí)現(xiàn)Filter接口的類上使用標(biāo)注或web.xml中過濾器設(shè)置信息的代表對(duì)象突琳。如果在定義過濾器時(shí)設(shè)置了初始參數(shù),則可以通過FilterConfig的getInitParameter()方法來取得初始參數(shù)符相。

Filter接口的doFilter()方法則類似于Servlet接口的service()方法拆融。當(dāng)請(qǐng)求來到容器,而容器發(fā)現(xiàn)調(diào)用Servlet的service()方法前啊终,可以應(yīng)用某過濾器時(shí)镜豹,就會(huì)調(diào)用該過濾器的doFilter()方法±渡可以在doFilter()方法中進(jìn)行service()方法的前置處理趟脂,而后決定是否調(diào)用FilterChain的doFilter()方法。如果調(diào)用了FilterChain的doFilter()方法例衍,就會(huì)運(yùn)行下一個(gè)過濾器散怖,如果沒有下一個(gè)過濾器,就調(diào)用請(qǐng)求目標(biāo)Servlet的service()方法(這里實(shí)際上用到了責(zé)任鏈模式)肄渗。如果沒有調(diào)用FilterChain的doFilter()方法镇眷,則請(qǐng)求就不會(huì)繼續(xù)交給接下來的過濾器或目標(biāo)Servlet,這就是所謂的攔截請(qǐng)求(從Servlet的角度來看翎嫡,根本不知道瀏覽器有發(fā)出請(qǐng)求)欠动。

以下是一個(gè)簡(jiǎn)單的性能評(píng)測(cè)過濾器,用來記錄請(qǐng)求與響應(yīng)的時(shí)間差惑申。

@WebFilter(
        filterName="PerformanceFilter", 
        urlPatterns={"/*"},
        dispatcherTypes={
            DispatcherType.FORWARD,
            DispatcherType.INCLUDE,
            DispatcherType.REQUEST,
            DispatcherType.ERROR,DispatcherType.ASYNC
        },
        initParams={@WebInitParam(name="Site", value="菜鳥教程")}
        )
public class PerformanceFilter implements Filter {
    private FilterConfig config;

    public PerformanceFilter() {

    }

    public void destroy() {

    }

    public void doFilter(ServletRequest request, ServletResponse response, 
    FilterChain chain) throws IOException, ServletException {
        long begin = System.currentTimeMillis();
        chain.doFilter(request, response);
        config.getServletContext().log("Performance process in " + 
                (System.currentTimeMillis() - begin) + " milliseconds");
        // 輸出站點(diǎn)名稱
        System.out.println("站點(diǎn)網(wǎng)址:http://www.runoob.com");

    }

    public void init(FilterConfig fConfig) throws ServletException {
        // 獲取初始化參數(shù)
        this.config = fConfig;
        String site = config.getInitParameter("Site"); 
        // 輸出初始化參數(shù)
        System.out.println("PerformanceFilter init done! 網(wǎng)站名稱: " + site); 
    }
}

當(dāng)過濾器類被載入容器并實(shí)例化后具伍,容器會(huì)運(yùn)行其init()方法并傳入FilterConfig對(duì)象作為參數(shù)。過濾器的設(shè)置與Servlet的設(shè)置很類似圈驼,@WebFilter中的filterName設(shè)置過濾器名稱人芽,urlPatterns設(shè)置哪些URL請(qǐng)求必須應(yīng)用哪個(gè)過濾器,可應(yīng)用的URL模式與Servlet基本上相同绩脆,而”/*“表示應(yīng)用在所有的URL請(qǐng)求上萤厅。除了指定URL模式外橄抹,也可以指定Servlet名稱,這可以通過@WebFilter的servletNames來設(shè)置:

@WebFilter(filterName="PerformanceFilter", servletNames={"Servlet1","Servlet2"})

如果想一次符合所有的Servlet名稱惕味,可以使用星號(hào)(*)楼誓。如果在過濾器初始化時(shí),想要讀取一些參數(shù)名挥,可以在@WebFilter中使用@WebInitParam來設(shè)置initParams疟羹,例如:

@WebFilter(
        filterName="EncodingFilter",
        urlPatterns={"/encoding"},  
        initParams={
                @WebInitParam(name="ENCODING", value="UTF-8")
        })
public class EncodingFilter implements Filter {
    private String ENCODING;
    private FilterConfig config;

    public EncodingFilter() {

    }

    public void init(FilterConfig fConfig) throws ServletException {
        // TODO Auto-generated method stub
        config = fConfig;
        ENCODING = config.getInitParameter("ENCODING");
        // 輸出初始化參數(shù)
        System.out.println("EncodingFilter init done! ENCODING = " + ENCODING); 
    }
    ...
}

觸發(fā)過濾器的時(shí)機(jī),默認(rèn)是瀏覽器直接發(fā)出請(qǐng)求時(shí)禀倔。如果是那些通過RequestDispatcher的forward()或include()發(fā)出的請(qǐng)求榄融,需要設(shè)置@WebFilter的dispatcherTypes,例如:

@WebFilter(
        filterName="some", 
        urlPatterns={"/some"},
        dispatcherTypes={
            DispatcherType.FORWARD,
            DispatcherType.INCLUDE,
            DispatcherType.REQUEST,
            DispatcherType.ERROR,DispatcherType.ASYNC
        })

如果不設(shè)置任何dispatcherTypes救湖,則默認(rèn)為REQUEST剃袍。FORWARD就是指通過RequestDispatcher的forward()方法而來的請(qǐng)求可以套用過濾器,INCLUDE是指通過RequestDispatcher的include方法而來的請(qǐng)求可以套用過濾器捎谨,ERROR是指由容器處理例外而轉(zhuǎn)發(fā)過來的請(qǐng)求可以套用過濾器,ASYNC是指異步處理器的請(qǐng)求可以觸發(fā)過濾器憔维。

3涛救、實(shí)現(xiàn)請(qǐng)求封裝器

以下通過兩個(gè)例子,來說明請(qǐng)求封裝器的實(shí)現(xiàn)與應(yīng)用业扒,分別是特殊字符替換過濾器與編碼設(shè)置過濾器检吆。

1、實(shí)現(xiàn)字符替換過濾器
  假設(shè)有個(gè)留言板程序已經(jīng)上線并正常運(yùn)行中程储,但是發(fā)現(xiàn)蹭沛,有些用戶會(huì)在留言中輸入一些HTML標(biāo)簽≌吕穑基于安全性的考慮摊灭,不希望用戶輸入的HTML標(biāo)簽直接出現(xiàn)在留言中而被一些瀏覽器當(dāng)作HTML的一部分來解釋。例如败徊,并不希望用戶在留言中輸入<a href=”http://openhome.cc”>OpenHome.cc</a>這樣的信息帚呼。不希望在留言顯示中有超鏈接,希望將一些HTML字符過濾掉皱蹦,如將<煤杀、>這樣的角括號(hào)置換為HTML實(shí)體字符,可以使用過濾器的方式沪哺。但問題在于沈自,雖然可以使用HttpServletRequest的getParameter()取得請(qǐng)求參數(shù)值,但是沒有一個(gè)像setParameter()的方法辜妓,可以將處理過后的參數(shù)值重新設(shè)置給HttpServletRequest枯途。

所幸忌怎,有個(gè)HttpServletRequestWrapper幫我們實(shí)現(xiàn)了HttpServletRequest接口,只要繼承這個(gè)類柔袁,并編寫想要重新定義的方法即可呆躲。相對(duì)應(yīng)于ServletRequest接口,也有個(gè)ServletRequestWrapper類可以使用捶索。

以下范例通過繼承HttpServletRequestWrapper實(shí)現(xiàn)一個(gè)請(qǐng)求封裝器插掂,可以將請(qǐng)求參數(shù)中的HTML字符替換為HTML實(shí)體字符。

public class EscapeWrapper extends HttpServletRequestWrapper {

    public EscapeWrapper(HttpServletRequest request) {
        super(request);//必須調(diào)用父類構(gòu)造器腥例,將HttpServletRequest實(shí)例傳入
    }

    @Override
    public String getParameter(String name) {
        String value = getRequest().getParameter(name);
        return StringEscapeUtils.escapeHtml(value);   
        //將請(qǐng)求參數(shù)值進(jìn)行字符替換
    }

}

之后若有Servlet想取得請(qǐng)求參數(shù)值辅甥,都會(huì)調(diào)用getParameter()方法,所以這里重新定義這個(gè)方法燎竖,在此方法中璃弄,進(jìn)行字符替換動(dòng)作」够兀可以使用這個(gè)請(qǐng)求封裝器搭配過濾器夏块,以進(jìn)行字符過濾的服務(wù)。例如:

@WebFilter(
        filterName="EscapeFilter",
        urlPatterns={"/guestbook"},
        dispatcherTypes={
                DispatcherType.FORWARD,
                DispatcherType.INCLUDE,
                DispatcherType.REQUEST,
                DispatcherType.ERROR,DispatcherType.ASYNC
            })
public class EscapeFilter implements Filter {
    private FilterConfig config;

    public EscapeFilter() {

    }

    public void destroy() {
        System.out.println("EscapeFilter calling done!"); 
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        long begin = System.currentTimeMillis();
        HttpServletRequest requestWrapper = new EscapeWrapper((HttpServletRequest)request);
        chain.doFilter(requestWrapper, response);
        config.getServletContext().log("Request escaping HTML tags in " + 
                (System.currentTimeMillis() - begin) + " milliseconds");
    }

    public void init(FilterConfig fConfig) throws ServletException {
        this.config = fConfig;
        System.out.println("EscapeFilter init done!"); 
    }
}

2纤掸、實(shí)現(xiàn)編碼設(shè)置過濾器
  在之前的范例中脐供,如果要設(shè)置請(qǐng)求字符編碼,都是在個(gè)別Servlet中處理借跪≌海可以在過濾器中進(jìn)行字符編碼的統(tǒng)一設(shè)置,如果日后想要改變編碼掏愁,就不用每個(gè)Servlet逐一修改了歇由。
由于HttpServletRequest的setCharacterEncoding()方法針對(duì)的是請(qǐng)求的Body內(nèi)容,對(duì)于GET請(qǐng)求果港,必須在取得請(qǐng)求參數(shù)的字節(jié)陣列后沦泌,重新指定編碼來解析。這個(gè)需求與上一個(gè)范例類似辛掠,可搭配請(qǐng)求封裝器來實(shí)現(xiàn)赦肃。

public class EncodingWrapper extends HttpServletRequestWrapper {
    private String ENCODING;

    public EncodingWrapper(HttpServletRequest request, String ENCODING) {
        super(request);
        this.ENCODING = ENCODING;
    }

    @Override
    public String getParameter(String name){
        String value = getRequest().getParameter(name);
        if(value != null) {
            try {
                //Web容器默認(rèn)使用ISO-8859-1編碼格式
                byte[] b = value.getBytes("ISO-8859-1");
                value = new String(b, ENCODING);
            } catch(UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }
        return value;
    }

}
@WebFilter(
        filterName="EncodingFilter",
        urlPatterns={"/encoding"},
        dispatcherTypes={
                DispatcherType.FORWARD,
                DispatcherType.INCLUDE,
                DispatcherType.REQUEST,
                DispatcherType.ERROR,DispatcherType.ASYNC
            },                  
        initParams={
                @WebInitParam(name="ENCODING", value="UTF-8")
        })
public class EncodingFilter implements Filter {
    private String ENCODING;
    private FilterConfig config;

    public EncodingFilter() {

    }

    public void destroy() {

    }

    public void doFilter(ServletRequest request, ServletResponse response,
    FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest)request;
        if("GET".equals(req.getMethod())) {
            long begin = System.currentTimeMillis();
            req = new EncodingWrapper(req, ENCODING);
            chain.doFilter(req, response);
            config.getServletContext().log("GET Method Request Encoding process in " + (System.currentTimeMillis() - begin) + " milliseconds");
        } else {
            req.setCharacterEncoding(ENCODING);
            chain.doFilter(req, response);
        }
    }

    public void init(FilterConfig fConfig) throws ServletException {
        config = fConfig;
        ENCODING = config.getInitParameter("ENCODING");
        // 輸出初始化參數(shù)
        System.out.println("EncodingFilter init done! ENCODING = " + ENCODING); 
    }
}

請(qǐng)求參數(shù)的編碼設(shè)置是通過過濾器初始參數(shù)來設(shè)置的,并在過濾器初始化方法init()中讀取公浪,過濾器僅在GET請(qǐng)求以創(chuàng)建EncodingWrapper實(shí)例他宛,其他方法則通過HttpServletRequest的setCharacterEncoding()來設(shè)置編碼,最后都調(diào)用FilterChain的doFilter()方法傳入EncodingWrapper實(shí)例或原請(qǐng)求對(duì)象欠气。

3厅各、實(shí)現(xiàn)響應(yīng)封裝器
  在Servlet中,是通過HttpServletResponse對(duì)象來對(duì)瀏覽器進(jìn)行響應(yīng)的预柒,如果想要對(duì)響應(yīng)的內(nèi)容進(jìn)行壓縮處理队塘,就要想辦法讓HttpServletResponse對(duì)象具有壓縮處理的功能袁梗。前面介紹過請(qǐng)求封裝器的實(shí)現(xiàn),而在響應(yīng)封裝器的部分憔古,可以繼承HttpServletResponseWrapper類來對(duì)HttpServletResponse對(duì)象進(jìn)行封裝遮怜。

若要對(duì)瀏覽器進(jìn)行輸出響應(yīng),必須通過getWriter()取得PrintWriter鸿市,或是通過getOutputStream()取得ServletOutputStream锯梁。 所以針對(duì)壓縮輸出的需求,主要就是繼承HttpServletResponseWrapper類之后焰情,通過重新定義這兩個(gè)方法來達(dá)成陌凳。

在下面例子中,壓縮的功能采用GZIP格式内舟,這是瀏覽器可以授受的壓縮格式合敦,可以使用GZIPOutputStream類來實(shí)現(xiàn)。由于getWriter()的PrintWriter在創(chuàng)建時(shí)验游,也是必須使用到ServletOutputStream充岛,所以在這里先擴(kuò)展ServletOutputStream類,讓它具有壓縮的功能耕蝉。

public class GZipServletOutputStream extends ServletOutputStream {
    private GZIPOutputStream gzipOutputStream;

    public GZipServletOutputStream(ServletOutputStream servletOutputStream) throws IOException {
        this.gzipOutputStream = new GZIPOutputStream(servletOutputStream);
    }

    @Override
    public boolean isReady() {
        return false;
    }

    @Override
    public void setWriteListener(WriteListener listener) {

    }

    public GZIPOutputStream getGzipOutputStream(){
        return gzipOutputStream;
    }

    @Override
    public void write(int b) throws IOException {
        gzipOutputStream.write(b);  //輸出時(shí)通過gzipOutputStream來壓縮輸出
    }
}

在HttpServletResponse對(duì)象傳入Servlet的service()方法前崔梗,必須先封裝它,使得調(diào)用getOutputStream()時(shí)赔硫,可以取得這里所實(shí)現(xiàn)的GZipServletOutputStream對(duì)象,而調(diào)用getWriter()時(shí)盐肃,也可以利用GZipServletOutputStream對(duì)象來構(gòu)造PrintWriter對(duì)象爪膊。

public class CompressionWrapper extends HttpServletResponseWrapper {
    private GZipServletOutputStream gzServletOutputStream;
    private PrintWriter printWriter;

    public CompressionWrapper(HttpServletResponse response) {
        super(response);
    }

     @Override
    public ServletOutputStream getOutputStream() throws IOException {
        //響應(yīng)中已經(jīng)調(diào)用過getWriter,再調(diào)用getOutputStream就拋出異常
        if(printWriter != null) {
            throw new IllegalStateException();
        }
        if(null == gzServletOutputStream) {
            gzServletOutputStream = 
            new GZipServletOutputStream(getResponse().getOutputStream());
        }
        return gzServletOutputStream;
    }

     @Override
     public PrintWriter getWriter() throws IOException {
         //響應(yīng)中已經(jīng)調(diào)用過getOutputStream砸王,再調(diào)用getWriter就拋出異常
         if(gzServletOutputStream != null) {
             throw new IllegalStateException();
         }
         if(null == printWriter) {
             gzServletOutputStream = new GZipServletOutputStream(getResponse().getOutputStream());
             OutputStreamWriter osw = new OutputStreamWriter(
                     gzServletOutputStream, getResponse().getCharacterEncoding());
             printWriter = new PrintWriter(osw);
         }
         return printWriter;
     }

     //不實(shí)現(xiàn)此方法推盛,因?yàn)檎嬲妮敵鰰?huì)被壓縮,忽略原來的內(nèi)容長(zhǎng)度設(shè)置
     @Override
     public void setContentLength(int len){
     } 

     public GZIPOutputStream getGZIPOutputStream() {
         if(this.gzServletOutputStream == null)
             return null;
         return this.gzServletOutputStream.getGzipOutputStream();
     }

}

在上例中要注意谦铃,由于Servlet規(guī)范中規(guī)定耘成,在同一個(gè)請(qǐng)求期間,getWriter()與getOutputStream()只能擇一調(diào)用驹闰,否則必拋出IllegalStateException瘪菌,因此建議在實(shí)現(xiàn)響應(yīng)封裝器時(shí),也遵循這個(gè)規(guī)范嘹朗。因此在重新定義getOutputStream()與getWriter()方法時(shí)师妙,分別要檢查是否已經(jīng)存在PrintWriter與ServletOutputStream實(shí)例。

接下來就實(shí)現(xiàn)一個(gè)壓縮過濾器屹培,使用上面開發(fā)的CompressionWrapper來封裝原HttpServletResponse默穴。

@WebFilter(
        filterName="CompressionFilter",
        urlPatterns = { "/*" })
public class CompressionFilter implements Filter {
    private FilterConfig config;

    public CompressionFilter() {

    }

    public void destroy() {

    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
    throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest)request;
        HttpServletResponse res = (HttpServletResponse)response;
        String encodings = req.getHeader("accept-encoding");
        //檢查是否接受壓縮
        if((encodings != null) && (encodings.indexOf("gzip") > -1)) {
            long begin = System.currentTimeMillis();
            CompressionWrapper responseWrapper = new CompressionWrapper(res);
            responseWrapper.setHeader("content-encoding", "gzip");  
            //設(shè)置響應(yīng)內(nèi)容編碼為gzip
            chain.doFilter(request, responseWrapper);
            GZIPOutputStream gzipOutputStream = responseWrapper.getGZIPOutputStream();
            if(gzipOutputStream != null) {
                gzipOutputStream.finish(); 
                //調(diào)用GZIPOutputStream的finish方法完成壓縮輸出
            }
            config.getServletContext().log("gzip compression process in " + 
                    (System.currentTimeMillis() - begin) + " milliseconds");
        }
        else {
            chain.doFilter(request, response); 
            //不接受壓縮直接進(jìn)行下一個(gè)過濾器
        }
    }

    public void init(FilterConfig fConfig) throws ServletException {
        this.config = fConfig;
        System.out.println("CompressionFilter init done!"); 
    }
}

瀏覽器是否接受GZIP壓縮格式怔檩,可以通過檢查accept-encoding請(qǐng)求標(biāo)頭中是否包括gzip字符串來判斷。如果可以接受GZIP壓縮蓄诽,創(chuàng)建CompressionWrapper封裝原響應(yīng)對(duì)象薛训,并設(shè)置content-encoding響應(yīng)標(biāo)頭為gzip,這樣瀏覽器就會(huì)知道響應(yīng)內(nèi)容是GZIP壓縮格式仑氛。接著調(diào)用FilterChain的doFilter()時(shí)乙埃,傳入響應(yīng)對(duì)象為CompressionWrapper對(duì)象。當(dāng)FilterChain的doFilter()結(jié)束時(shí)调衰,必須調(diào)用GZIPOutputStream的finish()方法膊爪,這才會(huì)將GZIP后的資料從緩沖區(qū)全部移出并進(jìn)行響應(yīng)。

如果瀏覽器不接受GZIP壓縮格式嚎莉,則直接調(diào)用FilterChain的doFilter()米酬,這樣就可以讓不接受GZIP壓縮格式的客戶端也可以收到原有的響應(yīng)內(nèi)容。

轉(zhuǎn)載

https://blog.csdn.net/fuzhongmin05/article/details/72723969

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末趋箩,一起剝皮案震驚了整個(gè)濱河市赃额,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叫确,老刑警劉巖跳芳,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異竹勉,居然都是意外死亡飞盆,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門次乓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吓歇,“玉大人,你說我怎么就攤上這事票腰〕强矗” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵杏慰,是天一觀的道長(zhǎng)测柠。 經(jīng)常有香客問我,道長(zhǎng)缘滥,這世上最難降的妖魔是什么轰胁? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮朝扼,結(jié)果婚禮上软吐,老公的妹妹穿的比我還像新娘。我一直安慰自己吟税,他們只是感情好凹耙,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布姿现。 她就那樣靜靜地躺著,像睡著了一般肖抱。 火紅的嫁衣襯著肌膚如雪备典。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天意述,我揣著相機(jī)與錄音提佣,去河邊找鬼。 笑死荤崇,一個(gè)胖子當(dāng)著我的面吹牛拌屏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播术荤,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼倚喂,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了瓣戚?” 一聲冷哼從身側(cè)響起端圈,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎子库,沒想到半個(gè)月后舱权,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡仑嗅,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年宴倍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仓技。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鸵贬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出浑彰,到底是詐尸還是另有隱情恭理,我是刑警寧澤拯辙,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布郭变,位于F島的核電站,受9級(jí)特大地震影響涯保,放射性物質(zhì)發(fā)生泄漏诉濒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一夕春、第九天 我趴在偏房一處隱蔽的房頂上張望未荒。 院中可真熱鬧,春花似錦及志、人聲如沸片排。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽率寡。三九已至迫卢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間冶共,已是汗流浹背乾蛤。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留捅僵,地道東北人家卖。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像庙楚,于是被迫代替她去往敵國(guó)和親上荡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345