使用過濾器改進應用程式 一、過濾器的目的 過濾器是可以攔截訪問資源的請求、資源的響應或者同時攔截兩者的應用組件。過濾器可以檢測和修改請求和響應,同時也可以拒絕、重定向或轉發請求。javax.servlet.Filter介面實現了過濾器技術,使用HttpServletRequest和HttpServl ...
使用過濾器改進應用程式
一、過濾器的目的
過濾器是可以攔截訪問資源的請求、資源的響應或者同時攔截兩者的應用組件。過濾器可以檢測和修改請求和響應,同時也可以拒絕、重定向或轉發請求。javax.servlet.Filter介面實現了過濾器技術,使用HttpServletRequest和HttpServletResponse。過濾器可以在部署描述符中以以編程的方式聲明,它們可以有初始化參數並且可以訪問ServletContext。
日誌過濾器
在應用程式開發中,需要記錄所有應用程式的請求和每個請求的結果(狀態碼,長度等其它信息)。通常Web容器提供了請求日誌的機制,但如果需要在請求日誌中顯示出一些特有的信息,可以使用過濾記錄請求。
驗證過濾器
如果需要確保只有授權用戶才可以訪問應用程式,通常可以檢查每個請求的信息,保證用戶已登錄,過濾器可以通過將驗證和授權操作集中到一個位置的方式使工作變得簡單。
壓縮和加密過濾器
存在著網路帶寬有限而CPU資源充足的情況,通常在數據傳輸之前對數據進行壓縮。過濾器可以在收到請求時,請求保持不變,但在響應返回給用戶時,使用過濾器可以壓縮相應對象。
錯誤處理過濾器
對於Web用用程式而言,出現錯誤,是一個HTTP響應代碼500,一般還會伴隨著一個普通的HTML頁面,寫著“Internal Server Error”以及一些診斷信息。對於在本地運行的應用程式對開發者是有用的,但是對於遠程的應用程式來說是不必要的。需要通過過濾器給用戶顯示出更加友好的和通用的錯誤處理頁面,並記錄必要的錯誤信息。
二、創建、聲明和映射過濾器
創建過濾器就是實現Filter介面一樣,過濾器在初始化的時候將調用init方法,他可以訪問過濾器的配置初始化參數和ServletContext。當請求進入到過濾器中,doFilter方法將會被調用,它提供了對ServletRequest、ServletResponse和FilterChain對象的訪問。在doFilter中,可以拒絕請求或者調用FilterChain對象的doFilter方法,可以封裝請求和響應對象。
過濾器鏈
儘管只有一個Servlet可以處理請求,但是可以使用許多的過濾攔截請求。在過濾器鏈中每一個過濾器接受進入的請求並將它傳遞到下一個過濾鏈中,直到所有匹配的過濾器都處理完成,最終再將它傳入Servlet中。調用FilterChain.doFilter()將觸發過濾器鏈的持續執行。如果當前的過濾器沒有調用FilterChain.doFilter(),將把控制權返回值Servlet容器中。
映射到URL模式和Servlet名稱
同Servlet一樣,過濾器可以被映射到URL模式,這會決定哪個或哪些過濾器將攔截某個請求。任何匹配某個過濾器的URL模式的請求在被匹配的Servlet處理之前將首先進入該過濾器,通過使用URL模式,不僅可以攔截Servlet請求,還可以攔截其它資源。
但是當現在已經有多個URL已經映射到Servlet上,並且希望某些過濾器映射到這些URL上。與映射到URL上相反,可以將這些過濾器映射到一個或多個Servlet名稱上。
1.在部署描述符中使用<filter>和<filter-mapping>元素:
<filter> <filter-name>filterA</filter-name> <filter-class>com.wrox.AnyRequestFilter</filter-class> </filter> <filter-mapping> <filter-name>filterA</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
在聲明瞭過濾器之後,可以將它映射到任意數目的URL或Servlet名稱。當然過濾器URL映射還可以包含通配符。
2.使用註解聲明和映射過濾器
@WebFilter{ filterName = "myFilter", urlPatterns = {"/foo","/bar/*"}, serVletNames = {"myServlet"}, dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.ASYNC} }
但是不可以對對過濾器鏈上的過濾器進行排序
三、過濾器排序
過濾器的順序決定了過濾器在過濾器鏈中出現的位置,這裡將會使用部署描述符來進行配置,因為註解無法進行排序配置。
URL模式映射和Servlet名稱映射,匹配請求的過濾器將按照它們出現在部署描述符或者編程式配置中的順序添加到過濾器鏈中,但是需要註意URL映射的過濾器優先順序比Servlet名稱映射到的過濾器高,由URL模式匹配的過濾器總是出現在有Servlet名稱匹配的過濾器之前。
<filter> <filter-name>filterA</filter-name> <filter-class>com.wrox.FilterA</filter-class> </filter> <filter-mapping> <filter-name>filterA</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>filterB</filter-name> <filter-class>com.wrox.FilterB</filter-class> </filter> <filter-mapping> <filter-name>filterB</filter-name> <url-pattern>/servletTwo</url-pattern> <url-pattern>/servletThree</url-pattern> </filter-mapping> <filter> <filter-name>filterC</filter-name> <filter-class>com.wrox.FilterC</filter-class> </filter> <filter-mapping> <filter-name>filterC</filter-name> <url-pattern>/servletTwo</url-pattern> </filter-mapping>
這是一個filter的實例:
這是處理中的第一個過濾器,它將記錄處理請求的時間,並記錄所有訪問應用程式的請求信息——IP地址、時間戳、請求方法等信息,finally塊中是日誌的操作。
public class RequestLogFilter implements Filter
{
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException
{
Instant time = Instant.now();
StopWatch timer = new StopWatch();
try
{
timer.start();
chain.doFilter(request, response);
}
finally
{
timer.stop();
HttpServletRequest in = (HttpServletRequest)request;
HttpServletResponse out = (HttpServletResponse)response;
String length = out.getHeader("Content-Length");
if(length == null || length.length() == 0)
length = "-";
System.out.println(in.getRemoteAddr() + " - - [" + time + "]" +
" \"" + in.getMethod() + " " + in.getRequestURI() + " " +
in.getProtocol() + "\" " + out.getStatus() + " " + length +
" " + timer);
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
@Override
public void destroy() { }
}