標籤:filter httpservletrequestwrapper 裝飾模式 設計模式
一 簡介
如題所示,有時候我們需要在一個請求到達Controller之前能夠截獲其請求,並且根據其具體情況對 HttpServletRequest 中的參數進行過濾或者修改。這時,有的同學可能會想:我們是否可以在一個Filter中將 HttpServletRequest 裡的所有參數都取出來分別進行過濾然後再放回到該HttpServletRequest 中呢?
很顯然,在 HttpServletRequest 貌似只有 setAttribute(String name, Object o) 這個方法可以設定參數,但是我們經過嘗試之後可以發現:使用 setAttribute(String name, Object o) 方法來重新設定參數顯然是不行的,因為在Controller中擷取參數本質上還是調用的ServletRequest的public String getParameter(String name) 或者 public String[] getParameterValues(String name) 方法,因此想要達到“在Filter中修改HttpServletRequest的參數”的目的,顯然是需要使用裝飾模式來複寫這些方法才行的
在正式代碼之前,我還是先簡單介紹下ServletRequest、HttpServletRequest、ServletRequestWrapper以及HttpServletRequestWrapper這幾個介面或者類之間的層次關係,並說明“繼承HttpServletRequestWrapper類以實現在Filter中修改HttpServletRequest的參數”這種方式的原理是什麼
如果我們從網上下載tomcat的原始碼並查看的話,就可以很清楚地看到這幾個類之間的層次關係了,在eclipse中看,它們之間的層次關係是這樣的:
650) this.width=650;" src="http://s5.51cto.com/wyfs02/M02/8A/D0/wKioL1g85qyTt9gAAABZIaaToZg320.png" title="20160924212414740.png" alt="wKioL1g85qyTt9gAAABZIaaToZg320.png" />
如果這個圖表還不夠清楚地話,我還畫了一個簡單的UML結構圖:
650) this.width=650;" src="http://s5.51cto.com/wyfs02/M00/8A/D0/wKioL1g85t-A2X73AABLKAbKiwo561.png" title="20160924212657710.png" alt="wKioL1g85t-A2X73AABLKAbKiwo561.png" />
註:因為我現在沒有下載專門的UML建模工具,因此就使用“畫圖”工具簡單地畫了一下類圖,同時這裡的ModifyParametersWrapper 是我後面舉例用到的的一個自訂的類
如果學過“裝飾模式”的童鞋可能已經發現了,上面這個關係毫無疑問是一個很標準的裝飾模式:
-
ServletRequest 抽象組件
-
HttpServletRequest 抽象組件的一個子類,它的執行個體被稱作“被裝飾者”
-
ServletRequestWrapper 一個基本的裝飾類,這裡是非抽象的
-
HttpServletRequestWrapper 一個具體的裝飾者,當然這裡也繼承了HttpServletRequest這個介面,是為了擷取一些在ServletRequest中沒有的方法
-
ModifyParametersWrapper 同樣是 一個具體的裝飾者(PS:我自訂的一個類)
註:一個標準的裝飾模式的UML類圖是這樣的:
650) this.width=650;" src="http://s2.51cto.com/wyfs02/M01/8A/D0/wKioL1g85wehL7sOAAAShJLhnak171.png" title="20160924213704413.png" alt="wKioL1g85wehL7sOAAAShJLhnak171.png" />
那麼問題來了,如何在Filter中修改後台Controller中擷取到的HttpServletRequest中的參數?
答:很簡單,只需要在Filter中自訂一個類繼承於HttpServletRequestWrapper,並複寫getParameterNames、getParameter、getParameterValues等方法即可
二 代碼實現
(1)自訂的過濾器ModifyParametersFilter.java:
package cn.zifangsky.filter;import java.io.IOException;import java.util.Enumeration;import java.util.Map;import java.util.Vector;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;import javax.servlet.http.HttpServletResponse;import org.springframework.web.filter.OncePerRequestFilter;public class ModifyParametersFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {ModifyParametersWrapper mParametersWrapper = new ModifyParametersWrapper(request);filterChain.doFilter(mParametersWrapper, response);}/** * 繼承HttpServletRequestWrapper,建立裝飾類,以達到修改HttpServletRequest參數的目的 */private class ModifyParametersWrapper extends HttpServletRequestWrapper {private Map<String, String[]> parameterMap; // 所有參數的Map集合public ModifyParametersWrapper(HttpServletRequest request) {super(request);parameterMap = request.getParameterMap();}// 重寫幾個HttpServletRequestWrapper中的方法/** * 擷取所有參數名 * * @return 返回所有參數名 */@Overridepublic Enumeration<String> getParameterNames() {Vector<String> vector = new Vector<String>(parameterMap.keySet());return vector.elements();}/** * 擷取指定參數名的值,如果有重複的參數名,則返回第一個的值 接收一般變數 ,如text類型 * * @param name * 指定參數名 * @return 指定參數名的值 */@Overridepublic String getParameter(String name) {String[] results = parameterMap.get(name);if (results == null || results.length <= 0)return null;else {System.out.println("修改之前: " + results[0]);return modify(results[0]);}}/** * 擷取指定參數名的所有值的數組,如:checkbox的所有資料 * 接收陣列變數 ,如checkobx類型 */@Overridepublic String[] getParameterValues(String name) {String[] results = parameterMap.get(name);if (results == null || results.length <= 0)return null;else {int length = results.length;for (int i = 0; i < length; i++) {System.out.println("修改之前2: " + results[i]);results[i] = modify(results[i]);}return results;}}/** * 自訂的一個簡單修改原參數的方法,即:給原來的參數值前面添加了一個修改標誌的字串 * * @param string * 原參數值 * @return 修改之後的值 */private String modify(String string) {return "Modified: " + string;}}}
上面的代碼很簡單,就是添加了一個內部類:ModifyParametersWrapper,然後複寫了ServletRequest中的幾個方法,具體來說就是將原來的每個參數的值的前面加上了“Modified: ”這個字串
(2)在web.xml中註冊該過濾器:
<filter><filter-name>ModifyParametersFilter</filter-name><filter-class>cn.zifangsky.filter.ModifyParametersFilter</filter-class></filter><filter-mapping><filter-name>ModifyParametersFilter</filter-name><url-pattern>/param/*</url-pattern><!-- 直接從用戶端過來的請求以及通過forward過來的請求都要經過該過濾器 --><dispatcher>REQUEST</dispatcher><dispatcher>FORWARD</dispatcher></filter-mapping>
(3)添加一個測試使用的Controller,即:TestModifyController.java:
package cn.zifangsky.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;@Controllerpublic class TestModifyController {@RequestMapping("/param/modify.html")public void modify(@RequestParam("name") String name){System.out.println("修改之後: " + name);}}
這裡沒有處理複雜的邏輯,僅僅只是簡單地輸出
(4)測試:
啟動項目之後訪問:http://localhost:9180/FilterDemo/param/modify.html?name=abc
可以發現,在控制台中輸出如下:
修改之前2: abc修改之後: Modified: abc
這就表明了我們前面自訂的過濾器已經將HttpServletRequest中原來的參數成功修改了。同時,還說明SpringMVC的@RequestParam註解本質上調用的是ServletRequest中的 getParameterValues(String name) 方法而不是 getParameter(String name) 方法
註:tomcat-8.5.5源碼:http://pan.baidu.com/s/1skWOso9
參考文章:
PS:上面圖片中的浮水印是我個人部落格的網域名稱,因此還請管理員手下留情不要給我標為“轉載文章”,謝謝!!!
本文出自 “zifangsky的個人部落格” 部落格,請務必保留此出處http://983836259.blog.51cto.com/7311475/1877592
繼承HttpServletRequestWrapper以實現在Filter中修改HttpServletRequest的參數