過濾器應用【編碼、敏感詞、壓縮、轉義過濾器】

前言

在上篇博文中,咱們已經講解了過濾器的基本概念,使用以及簡單的Servlet應用了。這篇博文主要講解過濾器的高級應用。。html

編碼過濾器

目的:解決全站的亂碼問題設計模式

開發過濾器

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {

        //將request和response強轉成http協議的
        HttpServletRequest httpServletRequest = (HttpServletRequest) req;
        HttpServletResponse httpServletResponse = (HttpServletResponse) resp;

        httpServletRequest.setCharacterEncoding("UTF-8");
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("text/html;charset=UTF-8");
        
        chain.doFilter(httpServletRequest, httpServletResponse);
    }

第一次測試

Servlet1中向瀏覽器迴應中文數據,沒有出現亂碼。瀏覽器

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        response.getWriter().write("看完博客點贊!");

    }

 

這裏寫圖片描述

 

分析

上面的過濾器是不完善的,由於瀏覽器用get方式提交給服務器的中文數據,單單靠上面的過濾器是沒法完成的!緩存

那麼咱們須要怎麼作呢??咱們以前解決get方式的亂碼問題是這樣的:使用request獲取傳遞過來的數據,通過ISO 8859-1反編碼獲取獲得不是亂碼的數據(傳到Servlet上的數據已經被ISO 8859-1編碼過了,反編碼就能夠獲取原來的數據),再用UTF-8編碼,獲得中文數據!服務器

參考我以前的博文:mp.weixin.qq.com/s?__biz=MzI…微信

在Servlet獲取瀏覽器以GET方式提交過來的中文是亂碼的根本緣由是:getParameter()方法是以ISO 8859-1的編碼來獲取瀏覽器傳遞過來的數據的,獲得的是亂碼app

既然知道了根本緣由,那也好辦了:過濾器傳遞的request對象,使用getParameter()方法的時候,獲取獲得的是正常的中文數據jsp

也就是說,sun公司爲咱們提供的request對象是不夠用的,由於sun公司提供的request對象使用getParameter()獲取get方式提交過來的數據是亂碼,因而咱們要加強request對象(使得getParameter()獲取獲得的是中文)!ide

加強request對象

加強request對象,咱們要使用包裝設計模式!post

包裝設計模式的五個步驟:

  • **一、實現與被加強對象相同的接口 **
  • 二、定義一個變量記住被加強對象
  • 三、定義一個構造器,接收被加強對象
  • 四、覆蓋須要加強的方法
  • 五、對於不想加強的方法,直接調用被加強對象(目標對象)的方法

sun公司也知道咱們可能對request對象的方法不滿意,因而提供了HttpServletRequestWrapper類給咱們實現(若是實現HttpServletRequest接口的話,要實現太多的方法了!)

class MyRequest extends HttpServletRequestWrapper {
	
	    private HttpServletRequest request;
	
	    public MyRequest(HttpServletRequest request) {
	        super(request);
	        this.request = request;
	    }
	
	    @Override
	    public String getParameter(String name) {
	        String value = this.request.getParameter(name);
	
	        if (value == null) {
	            return null;
	        }
	
	        //若是不是get方法的,直接返回就好了
	        if (!this.request.getMethod().equalsIgnoreCase("get")) {
	            return null;
	        }
	
	        try {
	
	            //進來了就說明是get方法,把亂碼的數據
	            value = new String(value.getBytes("ISO8859-1"), this.request.getCharacterEncoding());
	            return value ;
	
	        } catch (UnsupportedEncodingException e) {
	            e.printStackTrace();
	
	            throw new RuntimeException("不支持該編碼");
	        }
	
	    }
	}

將被加強的request對象傳遞給目標資源,那麼目標資源使用request調用getParameter()方法的時候,獲取獲得的就是中文數據,而不是亂碼了!

//將request和response強轉成http協議的
        HttpServletRequest httpServletRequest = (HttpServletRequest) req;
        HttpServletResponse httpServletResponse = (HttpServletResponse) resp;

        httpServletRequest.setCharacterEncoding("UTF-8");
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("text/html;charset=UTF-8");

        MyRequest myRequest = new MyRequest(httpServletRequest);

		//傳遞給目標資源的request是被加強後的。
        chain.doFilter(myRequest, httpServletResponse);

第二次測試

  • 使用get方式傳遞中文數據給服務器
<form action="${pageContext.request.contextPath}/Servlet1" method="get">

    <input type="hidden" name="username" value="中國">
    

    <input type="submit" value="提交">
</form>


 

這裏寫圖片描述

 

敏感詞的過濾器

若是用戶輸入了敏感詞(傻b、尼瑪、操蛋等等不文明語言時),咱們要將這些不文明用於屏蔽掉,替換成符號!

要實現這樣的功能也很簡單,用戶輸入的敏感詞確定是在getParameter()獲取的,咱們在getParameter()獲得這些數據的時候,判斷有沒有敏感詞彙,若是有就替換掉就行了!簡單來講:也是要加強request對象

加強request對象

class MyDirtyRequest extends HttpServletRequestWrapper {
	
	    HttpServletRequest request;
	
	    //定義一堆敏感詞彙
	    private List<String> list = Arrays.asList("傻b", "尼瑪", "操蛋");
	
	    public MyDirtyRequest(HttpServletRequest request) {
	        super(request);
	        this.request = request;
	    }
	
	    @Override
	    public String getParameter(String name) {
	
	        String value = this.request.getParameter(name);
	
	        if (value == null) {
	            return null;
	        }
	
	        //遍歷list集合,看看獲取獲得的數據有沒有敏感詞彙
	        for (String s : list) {
	
	            if (s.equals(value)) {
	                value = "*****";
	            }
	        }
	
	        return value ;
	    }
	}

開發過濾器

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {

        //將request和response強轉成http協議的
        HttpServletRequest httpServletRequest = (HttpServletRequest) req;
        HttpServletResponse httpServletResponse = (HttpServletResponse) resp;

        MyDirtyRequest dirtyRequest = new MyDirtyRequest(httpServletRequest);

        //傳送給目標資源的是被加強後的request對象
        chain.doFilter(dirtyRequest, httpServletResponse);
    }

測試

 

這裏寫圖片描述

 

壓縮資源過濾器

按照過濾器的執行順序:執行完目標資源,過濾器後面的代碼還會執行。因此,咱們在過濾器中能夠獲取執行完目標資源後的response對象!

咱們知道sun公司提供的response對象調用write()方法,是直接把數據返回給瀏覽器的。咱們要想實現壓縮的功能,write()方法就不能直接把數據寫到瀏覽器上!

這和上面是相似的,過濾器傳遞給目標資源的response對象就須要被咱們加強,使得目標資源調用writer()方法的時候不把數據直接寫到瀏覽器上

加強response對象

response對象可能會使用PrintWriter或者ServletOutputStream對象來調用writer()方法的,因此咱們加強response對象的時候,須要把getOutputSteam和getWriter()重寫

class MyResponse extends HttpServletResponseWrapper{
	
	    HttpServletResponse response;
	    public MyResponse(HttpServletResponse response) {
	        super(response);
	        this.response = response;
	    }
	
	
	    @Override
	    public ServletOutputStream getOutputStream() throws IOException {
	        return super.getOutputStream();
	    }
	
	    @Override
	    public PrintWriter getWriter() throws IOException {
	        return super.getWriter();
	    }
	}

接下來,ServletOutputSteam要調用writer()方法,使得它不會把數據寫到瀏覽器上。這又要咱們加強一遍了!

加強ServletOutputSteam

/*加強ServletOutputSteam,讓writer方法不把數據直接返回給瀏覽器*/
	class MyServletOutputStream extends ServletOutputStream{
	
	    private ByteArrayOutputStream byteArrayOutputStream;
	
	    public MyServletOutputStream(ByteArrayOutputStream byteArrayOutputStream) {
	        this.byteArrayOutputStream = byteArrayOutputStream;
	    }
	
	    //當調用write()方法的時候,實際上是把數據寫byteArrayOutputSteam上
	    @Override
	    public void write(int b) throws IOException {
	        this.byteArrayOutputStream.write(b);
	
	    }
	}

加強PrintWriter

PrintWriter對象就好辦了,它原本就是一個包裝類,看它的構造方法,咱們直接能夠把ByteArrayOutputSteam傳遞給PrintWriter上。

 

這裏寫圖片描述

 

@Override
    public PrintWriter getWriter() throws IOException {
        printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, this.response.getCharacterEncoding()));

        return printWriter;
    }

獲取緩存數據

咱們把數據都寫在了ByteArrayOutputSteam上了,應該提供方法給外界過去緩存中的數據!

public byte[] getBuffer() {

        try {

            //防止數據在緩存中,要刷新一下!
            if (printWriter != null) {
                printWriter.close();
            }
            if (byteArrayOutputStream != null) {
                byteArrayOutputStream.flush();
                return byteArrayOutputStream.toByteArray();
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

加強response的完整代碼

class MyResponse extends HttpServletResponseWrapper{

    private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

    private PrintWriter printWriter ;

    private HttpServletResponse response;
    public MyResponse(HttpServletResponse response) {
        super(response);
        this.response = response;
    }


    @Override
    public ServletOutputStream getOutputStream() throws IOException {

        //這個的ServletOutputSteam對象調用write()方法的時候,把數據是寫在byteArrayOutputSteam上的
        return new MyServletOutputStream(byteArrayOutputStream);
    }

    @Override
    public PrintWriter getWriter() throws IOException {
        printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, this.response.getCharacterEncoding()));

        return printWriter;
    }

    public byte[] getBuffer() {

        try {

            //防止數據在緩存中,要刷新一下!
            if (printWriter != null) {
                printWriter.close();
            }
            if (byteArrayOutputStream != null) {
                byteArrayOutputStream.flush();
                return byteArrayOutputStream.toByteArray();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

過濾器

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {


        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        MyResponse myResponse = new MyResponse(response);

        //把被加強的response對象傳遞進去,目標資源調用write()方法的時候就不會直接把數據寫在瀏覽器上了
        chain.doFilter(request, myResponse);

        //獲得目標資源想要返回給瀏覽器的數據
        byte[] bytes = myResponse.getBuffer();

        //輸出原來的大小
        System.out.println("壓縮前:"+bytes.length);

        
        //使用GZIP來壓縮資源,再返回給瀏覽器
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
        gzipOutputStream.write(bytes);
        
        //獲得壓縮後的數據
        byte[] gzip = byteArrayOutputStream.toByteArray();
        
        System.out.println("壓縮後:" + gzip.length);
        
        //還要設置頭,告訴瀏覽器,這是壓縮數據!
        response.setHeader("content-encoding", "gzip");
        response.setContentLength(gzip.length);
        response.getOutputStream().write(gzip);
     
    }


 

測試

  • 在Servlet上輸出一大段文字:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        response.getWriter().write("fdshfidsuhfidusfhuidsfhuidshdsuifhsd" +
                "uifhsduifffffdshfidsuhfidusfhuidsfhuidshdsuif" +
                "hsduifhsduifffffdshfidsuhfidusfhuidsfhuidshd" +
                "suifhsduifhsduifffffdshfidsuhfidusfhuidsfhuidsh" +
                "dsuifhsduifhsduifffffdshfidsuhfidusfhuidsfhuids" +
                "hdsuifhsduifhsduifffffdshfidsuhfidusfhuidsfhuid" +
                "shdsuifhsduifhsduiffdshfidsuhfidusfhuidsfhuids" +
                "hdsuifhsduifhsduifffffdshfidsuhfidusfhuidsfhui" +
                "dshdsuifhsduifhsduifffffdshfidsuhfidusfhuidsfh" +
                "uidshdsuifhsduifhsduifffffdshfidsuhfidusfhuids" +
                "fhuidshdsuifhsduifhsduifffffdshfidsuhfidusfhuid" +
                "sfhuidshdsuifhsduifhsduifffffdshfidsuhfidusfhui" +
                "dsfhuidshdsuifhsduifhsduifffffdshfidsuhfidusfh" +
                "uidsfhuidshdsuifhsduifhsduifffffdshfidsuhfidusf" +
                "huidsfhuidshdsuifhsduifhsduifffffdshfidsuhfidus" +
                "fhuidsfhuidshdsuifhsduifhsduifffffdshfidsuhfid" +
                "usfhuidsfhuidshdsuifhsduifhsduifffffdshfidsuhf" +
                "idusfhuidsfhuidshdsuifhsduifhsd" +
                "uifffffdshfidsuhfidusfhuidsfhuidshdsuifhsduifhsduifffffff");

    }
  • 效果:

 

這裏寫圖片描述

 

HTML轉義過濾器

只要把getParameter()獲取獲得的數據轉義一遍,就能夠完成功能了。

加強request

class MyHtmlRequest extends HttpServletRequestWrapper{

    private HttpServletRequest request;

    public MyHtmlRequest(HttpServletRequest request) {
        super(request);
        this.request = request;
    }


    @Override
    public String getParameter(String name) {

        String value = this.request.getParameter(name);
        return this.Filter(value);
        
    }

    public String Filter(String message) {
        if (message == null)
            return (null);

        char content[] = new char[message.length()];
        message.getChars(0, message.length(), content, 0);
        StringBuffer result = new StringBuffer(content.length + 50);
        for (int i = 0; i < content.length; i++) {
            switch (content[i]) {
                case '<':
                    result.append("&lt;");
                    break;
                case '>':
                    result.append("&gt;");
                    break;
                case '&':
                    result.append("&amp;");
                    break;
                case '"':
                    result.append("&quot;");
                    break;
                default:
                    result.append(content[i]);
            }
        }
        return (result.toString());
        
    }
    
}

過濾器

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {



        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        MyHtmlRequest myHtmlRequest = new MyHtmlRequest(request);

        //傳入的是被加強的request!
        chain.doFilter(myHtmlRequest, response);

    }

測試

jsp代碼:

<form action="${pageContext.request.contextPath}/Servlet1" method="post">
	
	
	    <input type="hidden" name="username" value="<h1>你好i好<h1>">
	
	    <input type="submit" value="提交">
	</form>

Servlet代碼:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String value = request.getParameter("username");
        response.getWriter().write(value);

    }

 

這裏寫圖片描述

 

緩存數據到內存中

在前面咱們已經作過了,讓瀏覽器不緩存數據【驗證碼的圖片是不該該緩存的】。

如今咱們要作的是:緩存數據到內存中【若是某個資源重複使用,不輕易變化,應該緩存到內存中】

這個和壓縮數據的Filter很是相似的,由於讓數據不直接輸出給瀏覽器,把數據用一個容器(ByteArrayOutputSteam)存起來。若是已經有緩存了,就取緩存的。沒有緩存就執行目標資源!

加強response對象

class MyResponse extends HttpServletResponseWrapper {

    private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

    private PrintWriter printWriter ;

    private HttpServletResponse response;
    public MyResponse(HttpServletResponse response) {
        super(response);
        this.response = response;
    }


    @Override
    public ServletOutputStream getOutputStream() throws IOException {

        //這個的ServletOutputSteam對象調用write()方法的時候,把數據是寫在byteArrayOutputSteam上的
        return new MyServletOutputStream(byteArrayOutputStream);
    }

    @Override
    public PrintWriter getWriter() throws IOException {
        printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, this.response.getCharacterEncoding()));

        return printWriter;
    }

    public byte[] getBuffer() {

        try {

            //防止數據在緩存中,要刷新一下!
            if (printWriter != null) {
                printWriter.close();
            }
            if (byteArrayOutputStream != null) {
                byteArrayOutputStream.flush();
                return byteArrayOutputStream.toByteArray();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}


//加強ServletOutputSteam,讓writer方法不把數據直接返回給瀏覽器

class MyServletOutputStream extends ServletOutputStream {

    private ByteArrayOutputStream byteArrayOutputStream;

    public MyServletOutputStream(ByteArrayOutputStream byteArrayOutputStream) {
        this.byteArrayOutputStream = byteArrayOutputStream;
    }

    //當調用write()方法的時候,實際上是把數據寫byteArrayOutputSteam上
    @Override
    public void write(int b) throws IOException {
        this.byteArrayOutputStream.write(b);

    }
}

過濾器

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {

        //定義一個Map集合,key爲頁面的地址,value爲內存的緩存
        Map<String, byte[]> map = new HashMap<>();

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        //獲得客戶端想要請求的資源
        String uri = request.getRequestURI();
        byte[] bytes = map.get(uri);

        //若是有緩存,直接返回給瀏覽器就好了,就不用執行目標資源了
        if (bytes != null) {
            response.getOutputStream().write(bytes);
            return ;
        }

        //若是沒有緩存,就讓目標執行
        MyResponse myResponse = new MyResponse(response);
        chain.doFilter(request, myResponse);

        //獲得目標資源想要發送給瀏覽器的數據
        byte[] b = myResponse.getBuffer();

        //把數據存到集合中
        map.put(uri, b);

        //把數據返回給瀏覽器
        response.getOutputStream().write(b);


    }

測試

儘管是刷新,獲取獲得的也是從緩存拿到的數據!

 

這裏寫圖片描述

 

若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章的同窗,能夠關注微信公衆號:Java3y

相關文章
相關標籤/搜索