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

過濾器能夠動態的攔截請求和響應,以變換或使用包含在請求或響應中的信息。html

過濾器是可用於Servlet編程的Java類,能夠實現如下目的:java

  • 在客戶端的請求訪問後端資源以前,攔截這些請求。
  • 在服務器的響應發送回客戶端以前,處理這些響應。

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

當 Web 容器啓動 Web 應用程序時,它會爲你在部署描述符中聲明的每個過濾器建立一個實例。spring

Filter的執行順序與在web.xml配置文件中的配置順序一致,通常把Filter配置在全部的Servlet以前。編程

1.編寫過濾器

如何編寫過濾器?後端

  1. 建立一個類,必須實現Filter接口
  2. 在web.xml中進行配置,通常把Filter配置在全部的Servlet配置以前

方法介紹:tomcat

  • void init():Filter的初始化,Filter在服務器啓動時就建立,建立以後立刻執行這個方法。用來初始化一些參數
  • void doFilter(req,resp,chain):當向服務器請求的Servlet或jsp頁面在過濾器的過濾範圍內時就會執行這個方法。若方法體中沒有chain.doFilter()操做,則表示當向服務器請求該過濾器過濾範圍內的資源(如Servlet/JSP頁面/html頁面等)時,這些資源中的全部方法都不會執行(被過濾掉了);若方法體中有chain.doFilter()操做,表示不對過濾器過濾範圍內的資源進行過濾。(即你請求的資源下的方法會執行)
  • void destroy():在服務器關閉時對Filter進行銷燬,在Filter銷燬以前會執行這個方法,用來對非內存資源進行釋放。

對方法中設計到的類介紹:服務器

  • FilterConfig:與ServletConfig類似,該類有以下四個方法:
    • getInitParameter():獲取初始化參數。
    • getInitParameterNames():獲取全部初始化參數的名稱。
    • getFilterName():獲取過濾器的配置名稱。
    • getServletContext():獲取application。
  • FilterChain類:該類中有一個方法:
    • doFilter():是否是會以爲該方法與Filter接口中的doFilter()方法是同樣的呢?沒錯,兩者雖然外觀看起來同樣,但功能倒是千差萬別的。該方法被FilterChain對象調用,表示對Filter過濾器過濾範圍下的資源進行放行。

2.多過濾器的執行順序

Web應用程序能夠根據特定的目的定義若干個不一樣的過濾器,那麼就須要在web.xml中對多個過濾器進行多個配置。而在web.xml中使用<filter-mapping>來控制多個過濾器的執行順序,即哪一個過濾器的<filter-mapping>配置在web.xml中的順序排在前面那這個過濾器就先執行。app

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

  • 1.攔截直接請求方式:REQUEST
  • 2.攔截請求轉發方式:FORWARD
  • 3.攔截請求包含方式:INCLUDE
  • 4.攔截錯誤轉發方式:ERROR

實現不一樣的攔截方式須要在中進行不一樣的配置:框架

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

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

4.過濾器的應用場景

1.執行目標資源以前作」預處理」工做,例如設置編碼,這種一般都會放行,只是在目標資源執行以前作一些準備工做。(例如:幾乎是全部的Servlet中都須要寫request.setCharacteEncoding(),能夠把它放入到一個Filter中。)這種過濾器沒有攔截功能。

2.經過條件判斷是否放行,例如校驗當前用戶是否已經登陸,或者用戶IP是否已經被禁用。(有攔截操做) (粗粒度權限控制,會員有會員的權利、遊客有遊客的權利)

3.在目標資源執行後,作一些後續的特殊處理工做。例如把目標資源輸出的數據進行處理。

5.案例1:分IP統計網站的訪問次數

功能分析:1.統計工做須要在全部資源以前都執行,那麼就能夠放到Filter中了。2.咱們這個過濾器不打算作攔截操做,由於咱們只是用來作統計的。3.用什麼東西來裝載統計的數據。Map,整個網站只須要一個Map便可4.Map何時建立(使用ServletContextListener,在服務器啓動時完成建立,並保存到SevletContext中),Map保存到哪裏:Map須要在Filter中用來保存數據;Map須要在頁面使用,打印Map中的數據。

AListener.java:

AFilter.java:

show.jsp:

效果圖:

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

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

試想:之後的開發中每每會用到不少的servlet,那咱們豈不是要在每個servlet的doPost()和doGet方法中都寫上上述的解決編碼代碼?這時候咱們就能夠經過過濾器來解決了。

首先附上頁面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<%@ 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>

 

顯示在網頁上的界面爲:

經過」點擊這裏」的連接咱們便完成了經過jsp頁面向servlet發送GET請求參數,經過」提交」按鈕咱們便完成了經過jsp頁面向servlet發送POST請求參數。建立一個servlet,咱們在servlet中完成響應參數編碼的問題:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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");

    }
}

 

接下來在過濾器中完成請求參數編碼的問題,建立一個過濾器Filter,在web.xml中註冊:

1
2
3
4
5
6
7
8
9
<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中編碼爲:

1
2
3
4
5
6
7
8
9
10
11
12
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請求參數的編碼設置咱們直接在doFilter()方法體中添加request.setCharacterEncoding("utf-8");代碼便可(此時運行程序,POST請求參數編碼的問題成功解決),對於GET請求參數的編碼,有些同窗會以爲直接在doFilter()方法體中添加

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

 

便可。這樣的參數是不太靠譜的,由於這裏咱們知道要傳遞的請求參數爲username因此這裏能夠明瞭的指出,之後咱們不知道請求參數爲何或者請求參數有不少時那就須要更多的上訴代碼,因此這裏咱們採用裝飾者模式對request進行裝飾(即將原本的request換成咱們本身寫的request),建立一個EncodingRequest.java繼承HttpServletRequestWrapper,代碼以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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;
    }
}

 

在構造方法中,咱們傳入系統的request,而後將這個request賦值給咱們本身編寫的req,而後在重寫的getParameter()方法中經過咱們本身寫的req獲取請求參數並解決編碼問題,而後返回解決完編碼後的參數value(此時這個中文參數已解決編碼),而後在Filer中對咱們本身編寫的request(即Encodingquest對象)放行便可。如今doFilter()方法的方法體爲:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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");
        /**
         * 調包request
         * 1.寫一個request的裝飾類
         * 2.在放行時,使用咱們本身的request
         */

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

 

運行程序,成功解決GET請求方式的編碼問題,可是POST請求方式的編碼又出現了問題,這是爲何呢?由於咱們在doFilter方法中已經經過代碼request.setCharacterEncoding("utf-8");處理了POST請求方式的編碼問題,可是此時的請求是系統的request對象而不是咱們本身寫的req,咱們對req進行了放行而沒有對request進行方式,因此方法體中應該增長if判斷語句,改正後的doFilter()方法體內容爲:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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");

        /**
         * 調包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請求參數的編碼問題時直接在web.xml中添加以下配置而不用再寫一個過濾器:

1
2
3
4
5
6
7
8
9
10
11
12
<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配置文件添加編碼與工程編碼一致,以下:

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

 

2.對參數進行從新編碼:

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

 

第二種方法須要對每一個參數都進行從新編碼,比較麻煩。

迴歸咱們的過濾器講解,經過如上包裝request的方式即可以經過過濾器解決全站編碼問題。

相關文章
相關標籤/搜索