JavaWeb三大組件之過濾器(Filter)

title: JavaWeb三大組件之過濾器(Filter)
tags: JavaWeb 過濾器
categories: JavaWeb 過濾器


若圖片無法顯示,請前往我的博客查看,相應文章鏈接:http://codingxiaxw.cn/2016/10/27/27-JavaWeb%E4%B8%89%E5%A4%A7%E7%BB%84%E4%BB%B6%E4%B9%8B%E8%BF%87%E6%BB%A4%E5%99%A8/

過濾器可以動態(tài)的攔截請求和響應,以變換或使用包含在請求或響應中的信息。

過濾器是可用于Servlet編程的Java類,可以實現(xiàn)以下目的:

  • 在客戶端的請求訪問后端資源之前,攔截這些請求。
  • 在服務器的響應發(fā)送回客戶端之前,處理這些響應。

過濾器通過 Web 部署描述符(web.xml)中的 XML 標簽來聲明,然后映射到你的應用程序的部署描述符中的 Servlet 名稱或 URL 模式。

當 Web 容器啟動 Web 應用程序時,它會為你在部署描述符中聲明的每一個過濾器創(chuàng)建一個實例。

Filter的執(zhí)行順序與在web.xml配置文件中的配置順序一致,一般把Filter配置在所有的Servlet之前。

1.編寫過濾器

如何編寫過濾器?

  1. 創(chuàng)建一個類,必須實現(xiàn)Filter接口
  2. 在web.xml中進行配置,一般把Filter配置在所有的Servlet配置之前
image

方法介紹:

  • void init():Filter的初始化,F(xiàn)ilter在服務器啟動時就創(chuàng)建,創(chuàng)建之后馬上執(zhí)行這個方法。用來初始化一些參數(shù)
  • void doFilter(req,resp,chain):當向服務器請求的Servlet或jsp頁面在過濾器的過濾范圍內(nèi)時就會執(zhí)行這個方法。若方法體中沒有chain.doFilter()操作,則表示當向服務器請求該過濾器過濾范圍內(nèi)的資源(如Servlet/JSP頁面/html頁面等)時,這些資源中的所有方法都不會執(zhí)行(被過濾掉了);若方法體中有chain.doFilter()操作,表示不對過濾器過濾范圍內(nèi)的資源進行過濾。(即你請求的資源下的方法會執(zhí)行)
  • void destroy():在服務器關(guān)閉時對Filter進行銷毀,在Filter銷毀之前會執(zhí)行這個方法,用來對非內(nèi)存資源進行釋放。

對方法中設(shè)計到的類介紹:

  • FilterConfig:與ServletConfig相似,該類有如下四個方法:
    • getInitParameter():獲取初始化參數(shù)。
    • getInitParameterNames():獲取所有初始化參數(shù)的名稱。
    • getFilterName():獲取過濾器的配置名稱。
    • getServletContext():獲取application。
  • FilterChain類:該類中有一個方法:
    • doFilter():是不是會覺得該方法與Filter接口中的doFilter()方法是一樣的呢?沒錯,二者雖然外觀看起來一樣,但功能卻是千差萬別的。該方法被FilterChain對象調(diào)用,表示對Filter過濾器過濾范圍下的資源進行放行。

2.多過濾器的執(zhí)行順序

Web應用程序可以根據(jù)特定的目的定義若干個不同的過濾器,那么就需要在web.xml中對多個過濾器進行多個配置。而在web.xml中使用<filter-mapping>來控制多個過濾器的執(zhí)行順序,即哪個過濾器的<filter-mapping>配置在web.xml中的順序排在前面那這個過濾器就先執(zhí)行。

3.過濾器的四種攔截方式

  • 1.攔截直接請求方式:REQUEST
  • 2.攔截請求轉(zhuǎn)發(fā)方式:FORWARD
  • 3.攔截請求包含方式:INCLUDE
  • 4.攔截錯誤轉(zhuǎn)發(fā)方式:ERROR

實現(xiàn)不同的攔截方式需要在<filter-mapping>中進行不同的配置:

  • <dispatcher>REQUEST</dispatcher>
  • <dispatcher>FORWORD</dispatcher>
  • <dispatcher>INCLUDE</dispatcher>
  • <dispatcher>ERROR</dispatcher>

若在web.xml配置文件中沒有寫出上面四個攔截配置時默認該過濾器只攔截請求。

4.過濾器的應用場景

1.執(zhí)行目標資源之前做"預處理"工作,例如設(shè)置編碼,這種通常都會放行,只是在目標資源執(zhí)行之前做一些準備工作。(例如:幾乎是所有的Servlet中都需要寫request.setCharacteEncoding(),可以把它放入到一個Filter中。)這種過濾器沒有攔截功能。

2.通過條件判斷是否放行,例如校驗當前用戶是否已經(jīng)登錄,或者用戶IP是否已經(jīng)被禁用。(有攔截操作) (粗粒度權(quán)限控制,會員有會員的權(quán)利、游客有游客的權(quán)利)

3.在目標資源執(zhí)行后,做一些后續(xù)的特殊處理工作。例如把目標資源輸出的數(shù)據(jù)進行處理。

5.案例1:分IP統(tǒng)計網(wǎng)站的訪問次數(shù)

功能分析:1.統(tǒng)計工作需要在所有資源之前都執(zhí)行,那么就可以放到Filter中了。2.我們這個過濾器不打算做攔截操作,因為我們只是用來做統(tǒng)計的。3.用什么東西來裝載統(tǒng)計的數(shù)據(jù)。Map<String ,Integer>,整個網(wǎng)站只需要一個Map即可4.Map什么時候創(chuàng)建(使用ServletContextListener,在服務器啟動時完成創(chuàng)建,并保存到SevletContext中),Map保存到哪里:Map需要在Filter中用來保存數(shù)據(jù);Map需要在頁面使用,打印Map中的數(shù)據(jù)。

AListener.java:

image

AFilter.java:


image

show.jsp:

image

效果圖:

image

6.案例2:解決全站字符亂碼問題

一般我們通過jsp頁面請求轉(zhuǎn)發(fā)到servlet時,若請求方式為POST且請求參數(shù)包含中文參數(shù)時,我們需要在servlet的doPost()方法中設(shè)置POST請求編碼問題:request.setCharacterEncoding("utf-8");、設(shè)置響應編碼問題:response.setContentType("text/html;charset=utf-8");,這樣便可以解決post請求即響應編碼問題;而對于GET請求,若傳遞的請求參數(shù)包含中文參數(shù)時設(shè)置請求編碼就比較麻煩,需要在servlet的doGet()方法中設(shè)置響應編碼:response.setContentType("text/html;charset=utf-8");以及請求編碼:首先獲得傳遞給servlet的請求參數(shù):String username=request.getParameter("username")假設(shè)傳遞的請求參數(shù)為username,然后再輸入代碼username=new String(username.getBytes("ISO8859-1"),"utf-8");,這樣通過jsp頁面轉(zhuǎn)發(fā)到servlet的參數(shù)便解決了編碼問題。即可以通過response.getWrite().prinltn(username)正常顯示在網(wǎng)頁上。

試想:以后的開發(fā)中往往會用到很多的servlet,那我們豈不是要在每一個servlet的doPost()和doGet方法中都寫上上述的解決編碼代碼?這時候我們就可以通過過濾器來解決了。

首先附上頁面:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <a href="<c:url value="/AServlet?username=張三"/> ">點擊這里</a>

  <form action="<c:url value="/AServlet"/> " method="post">
    用戶名:<input type="text" name="username" value="李四">
    <input type="submit" value="提交">
  </form>
  </body>
</html>

顯示在網(wǎng)頁上的界面為:[圖片上傳失敗...(image-cfd911-1526285896147)]

通過"點擊這里"的鏈接我們便完成了通過jsp頁面向servlet發(fā)送GET請求參數(shù),通過"提交"按鈕我們便完成了通過jsp頁面向servlet發(fā)送POST請求參數(shù)。創(chuàng)建一個servlet,我們在servlet中完成響應參數(shù)編碼的問題:

public class AServlet extends HttpServlet {



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

        response.setContentType("text/html;charset=utf-8");

    }

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

        response.setContentType("text/html;charset=utf-8");

    }
}

接下來在過濾器中完成請求參數(shù)編碼的問題,創(chuàng)建一個過濾器Filter,在web.xml中注冊:

 <filter>
        <filter-name>Filter</filter-name>
        <filter-class>filter.Filter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

Filter中編碼為:

public class Filter implements javax.servlet.Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {

           }

    public void init(FilterConfig config) throws ServletException {

    }
}

對于POST請求參數(shù)的編碼設(shè)置我們直接在doFilter()方法體中添加request.setCharacterEncoding("utf-8");代碼即可(此時運行程序,POST請求參數(shù)編碼的問題成功解決),對于GET請求參數(shù)的編碼,有些同學會覺得直接在doFilter()方法體中添加

String  username=request.getParameter("username");username=new String(username.getBytes("ISO-8859-1"),"utf-8");

即可。這樣的參數(shù)是不太靠譜的,因為這里我們知道要傳遞的請求參數(shù)為username所以這里可以明了的指出,以后我們不知道請求參數(shù)為什么或者請求參數(shù)有很多時那就需要更多的上訴代碼,所以這里我們采用裝飾者模式對request進行裝飾(即將本來的request換成我們自己寫的request),創(chuàng)建一個EncodingRequest.java繼承HttpServletRequestWrapper,代碼如下:

public class EncodingRequest extends HttpServletRequestWrapper
{
    private HttpServletRequest req;

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

    @Override
    public String getParameter(String name) {
        String value=req.getParameter(name);


        //處理編碼問題
        try {
            value=new String(value.getBytes("ISO-8859-1"),"utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return value;
    }
}

在構(gòu)造方法中,我們傳入系統(tǒng)的request,然后將這個request賦值給我們自己編寫的req,然后在重寫的getParameter()方法中通過我們自己寫的req獲取請求參數(shù)并解決編碼問題,然后返回解決完編碼后的參數(shù)value(此時這個中文參數(shù)已解決編碼),然后在Filer中對我們自己編寫的request(即Encodingquest對象)放行即可?,F(xiàn)在doFilter()方法的方法體為:

 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        //處理post請求編碼問題
        request.setCharacterEncoding("utf-8");

        HttpServletRequest req= (HttpServletRequest) request;
        /**
         * 處理get請求的編碼問題
         */
//        String username=request.getParameter("username");
//        username=new String(username.getBytes("ISO-8859-1"),"utf-8");
        /**
         * 調(diào)包request
         * 1.寫一個request的裝飾類
         * 2.在放行時,使用我們自己的request
         */

            EncodingRequest er = new EncodingRequest(req);
            chain.doFilter(er, response);
}

運行程序,成功解決GET請求方式的編碼問題,但是POST請求方式的編碼又出現(xiàn)了問題,這是為什么呢?因為我們在doFilter方法中已經(jīng)通過代碼request.setCharacterEncoding("utf-8");處理了POST請求方式的編碼問題,但是此時的請求是系統(tǒng)的request對象而不是我們自己寫的req,我們對req進行了放行而沒有對request進行方式,所以方法體中應該增加if判斷語句,改正后的doFilter()方法體內(nèi)容為:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {

        //處理post請求編碼問題
        request.setCharacterEncoding("utf-8");

        HttpServletRequest req= (HttpServletRequest) request;
        /**
         * 處理get請求的編碼問題
         */
//        String username=request.getParameter("username");
//        username=new String(username.getBytes("ISO-8859-1"),"utf-8");

        /**
         * 調(diào)包request
         * 1.寫一個request的裝飾類
         * 2.在放行時,使用我們自己的request
         */
        if (req.getMethod().equals("GET")) {
            EncodingRequest er = new EncodingRequest(req);

            chain.doFilter(er, response);
        }else if (req.getMethod().equals("POST")){
            chain.doFilter(request, response);

        }
    }

此時運行程序,成功解決POST請求方式和GET請求方式的編碼問題。在學習框架之前我們都這樣通過Filter解決編碼問題,而當我們學習了Spring MVC框架后我們處理POST請求參數(shù)的編碼問題時直接在web.xml中添加如下配置而不用再寫一個過濾器:

<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

解決GET請求方式的編碼問題時有兩種解決方法:1.修改tomcat配置文件添加編碼與工程編碼一致,如下:

<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

2.對參數(shù)進行重新編碼:

String userName new 
String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")

第二種方法需要對每個參數(shù)都進行重新編碼,比較麻煩。

回歸我們的過濾器講解,通過如上包裝request的方式便可以通過過濾器解決全站編碼問題。

源碼請點擊這里前往我的github

2018.3.19更

歡迎加入我的Java交流1群:659957958。群里目前已有1800人,每天都非?;钴S,但為了篩選掉那些不懷好意的朋友進來搞破壞,所以目前入群方式已改成了付費方式,你只需要支付9塊錢,即可獲取到群文件中的所有干貨以及群里面各位前輩們的疑惑解答;為了鼓勵良好風氣的發(fā)展,讓每個新人提出的問題都得到解決,所以我將得到的入群收費收入都以紅包的形式發(fā)放到那些主動給新手們解決疑惑的朋友手中。在這里,我們除了談技術(shù),還談生活、談理想;在這里,我們?yōu)槟愕膶W習方向指明方向,為你以后的求職道路提供指路明燈;在這里,我們把所有好用的干貨都與你分享。還在等什么,快加入我們吧!

2018.4.21更:如果群1已滿或者無法加入,請加Java學習交流2群:305335626 。群2作為群1的附屬群,除了日常的技術(shù)交流、資料分享、學習方向指明外,還會在每年互聯(lián)網(wǎng)的秋春招時節(jié)在群內(nèi)發(fā)布大量的互聯(lián)網(wǎng)內(nèi)推方式,話不多說,快上車吧!

7.聯(lián)系

If you have some questions after you see this article,you can tell your doubts in the comments area or you can find some info by clicking these links.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 本文包括:1、Filter簡介2、Filter是如何實現(xiàn)攔截的?3、Filter開發(fā)入門4、Filter的生命周期...
    廖少少閱讀 7,512評論 3 56
  • 監(jiān)聽器(listener) 監(jiān)聽器簡介 :監(jiān)聽器就是一個實現(xiàn)特定接口的普通java程序,這個程序?qū)iT用于監(jiān)聽另一個...
    奮斗的老王閱讀 2,671評論 0 53
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • 爸爸、媽媽,是你們含辛茹苦把我養(yǎng)大。在我的成長過程中,讓你們歡喜,讓你們憂愁。你們有時和顏悅色地陪我玩兒、...
    凡間客閱讀 373評論 0 1
  • 原來還有這樣的活動。璐璐周末來上海做一個什么項目管理大會的志愿者。大家都在體驗呀。我還想報名黨委組織的交通協(xié)管員呢...
    bb2ec6e297b5閱讀 156評論 0 0

友情鏈接更多精彩內(nèi)容