Servlet中過濾器技術
Servlet中過濾器技術 基本概念 過濾器入門案例 API詳解 指定攔截 對請求進行攔截 攔截指定的類型 案例 在Jsp頁面中增加富文字編輯器
基本概念
3W1H原則:why(為什麼會有這個技術)、what(該技術的概念)、where(應用情境)、how(該技術怎麼用)。 為什麼要使用過濾器以及應用情境怎麼樣。
在項目的開發中,經常會寫重複的代碼,如在每一個Servlet中我們都會在代碼的開始處設定utf-8編碼,再如判斷使用者是否登入,在刪除、修改、添加情境中都會用到判斷邏輯,只有在登入之後才能操作,等等。很多情況下我們都會抽取重複的代碼,封裝為一個方法,並且需要手動的調用這些重複的方法。那麼就需要過濾器在請求到達Servlet之前首先進行過濾(所謂過濾就是公用代碼的執行),並且Servlet技術執行結束之後在回到過濾器,完成之後再經過Jsp層,起到過濾和開關的工作。 過濾器是什麼。
Filter就是過濾器,過濾器也是在tomcat中運行,並且在tomcat下只能運行Servlet程式,所以過濾器技術實際上也是Servlet技術,是Servlet技術中最激動人心的技術。 增加過濾器之後的執行流程
首先使用者訪問伺服器,接著過濾器對Servlet進行攔截,過濾器將Servlet請求處理完之後再允許存取,接著進入Servlet中,當然也能進入Jsp中(因為Jsp技術實際上也是Servlet技術),Servlet處理完成之後,再回到過濾器,最後過濾器將結果發送給瀏覽器做響應,過濾器形象的比喻就是回家的門! 過濾器入門案例 首先建立一個Servlet
//FilterServlet.javapackage com.jpzhutech.filter;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class FilterServlet extends HttpServlet { /** * The doGet method of the servlet. <br> * * This method is called when a form has its tag value method equals to get. * * @param request the request send by the client to the server * @param response the response send by the server to the client * @throws ServletException if an error occurred * @throws IOException if an error occurred */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); System.out.println("4.處理使用者請求開始"); } /** * The doPost method of the servlet. <br> * * This method is called when a form has its tag value method equals to post. * * @param request the request send by the client to the server * @param response the response send by the server to the client * @throws ServletException if an error occurred * @throws IOException if an error occurred */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); doGet(request, response); }} 建立過濾器類,實現Filter介面
//FilterDemo1.javapackage com.jpzhutech.filter;import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;public class FilterDemo1 implements Filter { // 建立過濾器執行個體,在伺服器啟動時執行 public FilterDemo1() { System.out.println("1.建立過濾器執行個體"); } //執行初始化方法,在伺服器啟動時執行 public void init(FilterConfig filterConfig) throws ServletException { System.out.println("2.執行過濾器初始化方法"); } //過濾器業務處理方法,在請求到達Servlet之前,先進入該方法處理公用的商務邏輯操作 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("3.執行過濾器業務處理方法"); //允許存取,去到Servlet chain.doFilter(request,response); //Servlet處理完成,又回到過濾器 System.out.println("5.Servlet處理完成,又回到過濾器"); } public void destroy() { System.out.println("6.銷毀過濾器執行個體"); }} 在web.xml中配置Filter
<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>filter</display-name> <filter> <filter-name>FilterDemo1</filter-name> <filter-class>com.jpzhutech.filter.FilterDemo1</filter-class> </filter> <filter-mapping> <filter-name>FilterDemo1</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <description>This is the description of my J2EE component</description> <display-name>This is the display name of my J2EE component</display-name> <servlet-name>FilterDemo1</servlet-name> <servlet-class>com.jpzhutech.filter.FilterDemo1</servlet-class> </servlet> <servlet> <description>This is the description of my J2EE component</description> <display-name>This is the display name of my J2EE component</display-name> <servlet-name>FilterServlet</servlet-name> <servlet-class>com.jpzhutech.filter.FilterServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>FilterDemo1</servlet-name> <url-pattern>/FilterDemo1</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>FilterServlet</servlet-name> <url-pattern>/FilterServlet</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list></web-app>
在瀏覽器訪問 localhost:8080/filte/FilterServlet 在控制台中會發現過濾器類中方法的調用順序
當有多個過濾器時,按照在web.xml檔案中的配置順序,依次進入不同的過濾器,之後看到允許存取代碼就進入到要執行的Servlet中,執行完Servlet之後回到過濾器的順序與進入的順序剛好相反
圖解上述過程
API詳解 涉及到的介面,其中的方法參見Servlet協助文檔
interface Filter 核心介面 interface FilterConfig interface FilterChain
請查閱Servlet文檔查看這三個介面中對應的方法。 指定攔截 對請求進行攔截
看web.xml設定檔中的一些具體內容:
<filter-mapping> <filter-name>FilterDemo1</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
其中url-pattern中/*代表攔截所有的資源(包括Servlet、Jsp、JS、Html、css、圖片等都是資源)請求,那麼我現在想做的是我能不能攔截指定的資源呢。比如說我現在只攔截Jsp資源,不攔截Servlet資源。怎麼做到呢。
<filter-mapping> <filter-name>FilterDemo1</filter-name> <url-pattern>/index.jsp</url-pattern> </filter-mapping>
上面的url-pattern=index.jsp代表的是只攔截index.jsp資源。如果我我想攔截多個資源該怎麼做呢。
<filter-mapping> <filter-name>FilterDemo1</filter-name> <url-pattern>/index.jsp</url-pattern> <url-pattern>/list.jsp</url-pattern> </filter-mapping>
同樣我可以寫成Regex的情況:
<filter-mapping> <filter-name>FilterDemo1</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping>
如果我們只攔截Servlet呢。
<filter-mapping> <filter-name>FilterDemo1</filter-name> <url-pattern>/index</url-pattern> </filter-mapping>
攔截指定的類型
<filter-mapping> <filter-name>FilterDemo1</filter-name> <dispatcher>REQUEST/ERROR/FORWARD/INCLUDE</dispatcher> </filter-mapping>
上面的配置執行個體表名只攔截請求/錯誤/轉寄/只攔截包含頁面。 案例
案例一:編碼統一處理,要求處理請求資料中的中文問題,請求分為GET/POST提交。因為每一個Servlet都要執行該步驟,所以我們將這個處理放到過濾器中執行,在Servlet中不再執行。
//EncodingFilter.javapackage com.jpzhutech.login;import java.io.IOException;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class EncodingFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); /**出現get中文亂碼是因為在request.getParameter方法內部沒有對提交方式判斷並處理 * String name = request.getParameter("userName"); * * 解決辦法:對指定的介面的某一個方法進行功能擴充,可以使用代碼,對request對象(也就是目標對象), * 建立代理對象 */ HttpServletRequest proxy = (HttpServletRequest) Proxy.newProxyInstance( request.getClass().getClassLoader(), // 指定當前使用的類載入器 new Class[]{HttpServletRequest.class}, // 對目標對象實現的介面類型 new InvocationHandler() { // 事件處理器 @Override public Object invoke(Object proxy, Method method, Object[] args) //method代表的是方法,後面的args是指前面method的值 throws Throwable { // 定義方法傳回值 Object returnValue = null; // 擷取方法名 String methodName = method.getName(); // 判斷:對getParameter方法進行GET提交中文處理 if ("getParameter".equals(methodName)) { // 擷取請求資料值 <input type="text" name="userName"> String value = request.getParameter(args[0].toString()); // 也就是得到userName的值 // 擷取提交方式 String methodSubmit = request.getMethod(); // 直接調用目標對象的方法 // 判斷如果是GET提交,需要對資料進行處理 (POST提交已經處理過了) if ("GET".equals(methodSubmit)) { if (value != null && !"".equals(value.trim())){ // 處理GET中文 value = new String(value.getBytes("ISO8859-1"),"UTF-8"); } } return value; //對get提交處理之後,返回處理後的值 } else { // 執行request對象的其他方法,只對getParamter方法進行處理 returnValue = method.invoke(request, args); } return returnValue; } }); chain.doFilter(proxy, response); //過濾器處理完成之後一定要允許存取,否則不會取執行Servlet代碼 } @Override public void destroy() { }}
//LoginServlet.javapackage com.jpzhutech.login;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class LoginServlet extends HttpServlet { /** * The doGet method of the servlet. <br> * * This method is called when a form has its tag value method equals to get. * * @param request the request send by the client to the server * @param response the response send by the server to the client * @throws ServletException if an error occurred * @throws IOException if an error occurred */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); //post方式提交時只需要加這一行代碼便能做到中文不亂碼 String name = request.getParameter("userName"); System.out.println("使用者名稱"+name); } /** * The doPost method of the servlet. <br> * * This method is called when a form has its tag value method equals to post. * * @param request the request send by the client to the server * @param response the response send by the server to the client * @throws ServletException if an error occurred * @throws IOException if an error occurred */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); }}
<--login.jsp--><%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><title>My JSP 'index.jsp' starting page</title><!-- 新 Bootstrap 核心 CSS 檔案 --><link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.css"><!-- 可選的Bootstrap主題檔案(一般不用引入) --><link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap-theme.min.css"><!-- jQuery檔案。務必在bootstrap.min.js 之前引入 --><script src="http://cdn.bootcss.com/jquery/1.11.1/jquery.min.js"></script><!-- 最新的 Bootstrap 核心 JavaScript 檔案 --><script src="http://cdn.bootcss.com/bootstrap/3.3.0/js/bootstrap.min.js"></script></head><body> <form action="${pageContext.request.contextPath}/LoginServlet" method="post"> 使用者名稱:<input type="text" name="userName"><br /> <input type="submit" value="post提交"> </form> <hr /> <form action="${pageContext.request.contextPath}/LoginServlet" method="get"> 使用者名稱:<input type="text" name="userName"><br /> <input type="submit" value="get提交"> </form></body></