草之戒_
          posts - 2,comments - 14,trackbacks - 0
                當我們在做web應用的時候都會處理客戶端提交到服務器的數據,如去除前后空格,一些非常字符,SQL注入類似的東西,在這里我主要說前后空格我是怎么來解決的,其它也都可以照此方法快速、方便、有效的解決,但是我一般對于非法字符,都是采用了標簽來解決它的,并沒有使用Filter轉義掉(純屬個人解決辦法)。
                去除前后空格看似非常簡單的事,但是有許多人可能就是因為這一個小問題,折磨自己半天,客戶端提交到所有的東西,都是以字符串形式提交的,我們不知道客戶是怎么操作的,他可能把一個age屬性對應的值,在輸入時多加了一個空格,而服務器age對應的卻是Integer類型,如果你使用servlet這事也好解決,但是如果你使用的是MVC框架,自動封裝時就會得到一個類型轉換異常,然而這個時候你是否有好的解決辦法呢?
                這里我使用Filter來解決這一問題,這是最簡單方便有效的解決方式,因為你不需要對每一個屬性在封裝前都去trim(),因為這是一件非常乏味的事情。大家都知道filter可以過濾我們想要它過濾的每一個請求,在這請求中有HttpServletRequest、HttpServletResponse。我們知道服務器取得客戶端發送的參數都是通過HttpServletRequest來獲取的,那我們可不可以在使用HttpServletRequest取值的時候就為每一個客戶端提交的屬性去除前后空格,或者其它的一些過濾操作。這肯定是可以的,那我們先來了解一下服務器是怎么取得客戶端的值的。
          1.getParameter(name),返回單個值。
          2.getParameterValues(name),返回一個數組。
          3.getParameterMap(),把客戶端提交參數封裝為一個Map返回。K:name,V:value。
                當我們使用servlet的時候一般都是使用前兩種,struts1使用的第2種,struts2(xwork)則使用的第3種,那么我們只要在這三個方法調用的時候處理前后空格,那么返回到服務器的參數就又減少了一分出bug的機會,看下面的實現。
          public class RequestParameterFilter implements Filter {

              
          private static Log log = LogFactory.getLog(RequestParameterFilter.class);

              
          private List<String> excludeNames;

              
          public void destroy() {

              }

              
          public void doFilter(ServletRequest request, ServletResponse response,
                      FilterChain chain) 
          throws IOException, ServletException {
                  
          //
                  request = new HttpServletRequestWrapper2((HttpServletRequest) request);
                  chain.doFilter(request, response);
              }

              
          public void init(FilterConfig config) throws ServletException {
                  String exclude 
          = config.getInitParameter("exclude");
                  
          // not is null.
                  if (exclude != null && exclude.length() > 0) {
                      excludeNames 
          = Arrays.asList(exclude.split(","));
                      
          if (log.isDebugEnabled()) {
                          log.debug(
          "initialize arguments.");
                      }
                  }
              }

              
          /**
               * 該類繼承之HttpServletRequestWrapper,并重寫了對應取得客戶端相當參數值的所有的方法。
               * <ul>
               * <li>getParameter</li>
               * <li>getParameterValues</li>
               * <li>getParameterMap</li>
               * </ul>
               * 
               * 
          @version 1.0/2010-6-10 上午11:25:47
               * 
          @author Aidan
               * 
          @see HttpServletRequestWrapper
               
          */
              
          private class HttpServletRequestWrapper2 extends HttpServletRequestWrapper {

                  
          private ParameterMap2 pm2;

                  
          public HttpServletRequestWrapper2(HttpServletRequest request) {
                      
          super(request);
                  }

                  
          public String getParameter(String name) {
                      
          if (excludeNames != null && excludeNames.contains(name)) {
                          
          return super.getParameter(name);
                      }
                      
          return trim(super.getParameter(name));
                  }

                  @SuppressWarnings(
          "unchecked")
                  
          public Map getParameterMap() {
                      
          // xwork便使用此方法取值
                      
          // 該方法返回一個Map,Map映射了客戶端請求對應的鍵值(K,V)。
                      if (pm2 == null) {
                          pm2 
          = new ParameterMap2(super.getParameterMap());
                      }
                      
          return pm2;
                  }

                  
          public String[] getParameterValues(String name) {
                      
          // Struts1使用此方法取得所有的參數值
                      if (excludeNames != null && excludeNames.contains(name)) {
                          
          return super.getParameterValues(name);
                      }
                      
          return (String[]) trim(super.getParameterValues(name));
                  }
              }

              
          /**
               * 該此繼承自HashMap。
               * 
               * 
          @version 1.0/2010-6-10 上午11:30:13
               * 
          @author Aidan
               * 
          @see HashMap
               
          */
              @SuppressWarnings( { 
          "unchecked""serial" })
              
          private class ParameterMap2 extends HashMap {

                  
          private Set entrySet;

                  
          /**
                   * 若要構造此類對象,則需要傳入一個map參數,該map對應的客戶端請求的參數(K,V)。
                   * 
                   * 
          @param map
                   *            映射客戶端參數。
                   
          */
                  
          public ParameterMap2(Map map) {
                      
          super(map);
                  }

                  
          public Set entrySet() {
                      
          // xwork使用了此方法取值
                      if (entrySet == null) {
                          entrySet 
          = new HashSet();
                          Set temSet 
          = super.entrySet();
                          
          for (Iterator iterator = temSet.iterator(); iterator.hasNext();) {
                              Map.Entry me 
          = (Map.Entry) iterator.next();
                              Entry2 entry 
          = new Entry2(me);
                              entrySet.add(entry);
                          }
                      }
                      
          return entrySet;
                  }

                  
          // 若直接從map使用key取得
                  public Object get(Object key) {
                      Object value 
          = super.get(key);
                      
          // 不過濾此對象
                      if (excludeNames != null && excludeNames.contains(key)) {
                          
          return value;
                      }
                      
          if (value != null) {
                          
          return trim(value);
                      }
                      
          return null;
                  }
              }

              @SuppressWarnings(
          "unchecked")
              
          private class Entry2<K, V> implements Map.Entry<K, V> {
                  
          private Map.Entry me;
                  
          private boolean isTrim = true;

                  
          public Entry2(Map.Entry me) {
                      
          if (me == null) {
                          
          throw new IllegalArgumentException(
                                  
          "Map.Entiry argument not null.");
                      }
                      
          this.me = me;
                      
          // 不過濾此對象
                      if (excludeNames != null && excludeNames.contains(me.getKey())) {
                          isTrim 
          = false;
                      }
                  }

                  
          public K getKey() {
                      
          return (K) me.getKey();
                  }

                  
          public V getValue() {
                      
          if (isTrim) {
                          
          return (V) trim(me.getValue());
                      }
                      
          return (V) me.getValue();
                  }

                  
          public V setValue(V value) {
                      
          return (V) me.setValue(value);
                  }

              }

              
          /**
               * 去除一個Object類型對應的前后空格,因為客戶端提交參數有兩種,一種:String,另一種:String[],此方法會自動判斷調用哪個方法。
               * 
               * 
          @param value
               *            需要處理的參數。
               * 
          @return 處理后的值。
               
          */
              
          protected Object trim(Object value) {
                  
          if (value instanceof String[]) {
                      
          return trim((String[]) value);
                  }
                  
          return trim(value.toString());
              }

              
          /**
               * 去除某個字符串的前后空格。
               * 
               * 
          @param value
               *            需要處理的參數。
               * 
          @return 處理后的值。
               
          */
              
          protected String trim(String value) {
                  
          if (value != null && value.length() > 0) {
                      
          return value.trim();
                  }
                  
          return value;
              }

              
          /**
               * 去除某個數組中所有的值的前后空格。
               * 
               * 
          @param values
               *            需要處理的數組。
               * 
          @return 處理后的值,當數組的length為1時,則返回一個String,反之返回一個數組。
               
          */
              
          protected Object trim(String[] values) {
                  
          if (values != null && values.length > 0) {
                      
          int len = values.length;
                      
          for (int i = 0; i < len; i++) {
                          values[i] 
          = trim(values[i]);
                      }
                  }
                  
          if (values.length == 1) {
                      
          return values[0];
                  }
                  
          return values;
              }

              
          /**
               * 
               * 
          @return 不處理的對象。
               
          */
              
          public List<String> getExcludeNames() {
                  
          return excludeNames;
              }

          }
                這個Filter實現原理非常簡單,我會過濾所有的請求,HttpServletRequestWrapper2繼承自HttpServletRequestWrapper,在構造函數中需要一個HttpServletRequest對象(這個request是web窗口創建的),然后我重載了上面所說的3個方法,在方法內部每次會去過濾當前值,這是利用了Java多態特性。在使用getParameterMap時較為麻煩,原理一樣。
                當然我們有時候可能有些特殊情況不需要過濾前后空格或者其它一些規則,這里我們可以使用exclude屬性來判斷是否過濾此屬性。
          DEMO:
          <form action="test!create.action" method="post">
                      name:
                      
          <input name="name" value=" My name is haha.. " />
                      
          <br />
                      
          <!-- This is a String,isn't number. -->
                      age:
                      
          <input name="age" value="   15  " />
                      
          <br />
                      email:
                      
          <input name="email" value=" grasszring@gmail.com " />
                      
          <br />
                      email2:
                      
          <input name="email" value=" grasszring@foxmail.com " />
                      
          <br />
                      
          <input type="submit" value=" submit " />
          </form>
          web.xml
          <filter>
                  
          <filter-name>requestParameter</filter-name>
                  
          <filter-class>com.onlyeffort.commons.web.filter.RequestParameterFilter</filter-class>
                  
          <init-param>
                      
          <!-- 不需要過濾此參數 -->
                      
          <param-name>exclude</param-name>
                      
          <param-value>email</param-value>
                  
          </init-param>
              
          </filter>
              
          <filter-mapping>
                  
          <filter-name>requestParameter</filter-name>
                  
          <url-pattern>/*</url-pattern>
          </filter-mapping>
          action
          @Action(params = { "actionName""test" })
          @Result(location 
          = "http://www.google.com", type = "redirect")
          @SuppressWarnings(
          "serial")
          public class TestController extends ActionSupport {

              
          private String name;
              
          private Integer age;
              
          private String[] email;
                  
          //.. get set method.

              @Override
              
          public String create() throws CreateFailureException {
                  System.out.println(name);
                  System.out.println(age);
                  
          for (String mail : email) {
                      System.out.println(mail);
                  }        
                  
          return SUCCESS;
              }
          }

                OK,如果大家有什么問題或有什么意見都盡管留言,感激不盡。


          轉載時請注明轉載地址,onlyeffort.QQ:501276913
          posted on 2010-06-10 14:45 Aidan 閱讀(2226) 評論(7)  編輯  收藏

          FeedBack:
          # re: 快速、方便、有效的Filter
          2010-06-11 09:29 | regale
          不錯!學習了  回復  更多評論
            
          # re: 快速、方便、有效的Filter
          2010-06-13 00:30 | zcl
          一個簡單的功能有必要搞得這么復雜嗎?雖然你這個filter可以去掉空格,也可以通過配置選擇不去掉哪些字段的空格,但你在action中還是要判斷傳過來的屬性是否為空,也就是說也要判斷形如:if (name != null)...,其實搞開發的已經習慣了這種風格的代碼:if (name != null &&!name.trim().equals(""))的形式了,這種寫法已經很通用了,可讀性很好!  回復  更多評論
            
          # re: 快速、方便、有效的Filter
          2010-06-13 08:36 | Aidan
          @zcl
          可能你沒有仔細查看我的例子,你仔細看age是一個Integer類型,但是client傳的卻是一個不能轉換為Integer的String字符串,這時候你使用MVC框架該如何?我在里面也清楚的說明了。  回復  更多評論
            
          # re: 快速、方便、有效的Filter
          2010-07-15 17:28 | jj
          如果只是去空格的話,filter大材小用了。與靈活性相比,我寧愿寫一個util來封裝requst取到的param。搞的這么復雜的確沒有必要  回復  更多評論
            
          # re: 快速、方便、有效的Filter
          2010-07-15 22:39 | Aidan
          @jj
          那我請問你,假如我們使用mvc框架的時候,你的util類如何使用??  回復  更多評論
            
          # re: 快速、方便、有效的Filter
          2010-07-16 00:07 | jj
          @Aidan
          與使用mvc框架什么關系?就不能用util類了?java.util的工具類你在mvc框架下從來不用?  回復  更多評論
            
          # re: 快速、方便、有效的Filter
          2010-07-16 00:35 | jj
          @Aidan
          還有,這么簡單的一個應用又何必小題大做爭論來爭論去呢?你的實現沒有問題,只不過是看有沒有必要,還有很多更簡潔更方便,至少exclude的時候不用重啟容器的方法。不同情況,不同考慮??傊@原本就是一個最最基本的問題,又有n多最最基本的解決方法,每種方法各有利弊,多說無益,相信有很多人瀏覽過卻沒有留言就是這個原因  回復  更多評論
            

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          編程部落   qq群:37996359(上限500人,一起關注java、討論技術,互相學習,共同進步)
          主站蜘蛛池模板: 盱眙县| 乐山市| 开远市| 万盛区| 内丘县| 济源市| 封丘县| 厦门市| 扶风县| 灵川县| 桐庐县| 靖江市| 德惠市| 天柱县| 浠水县| 巩留县| 益阳市| 芷江| 阳新县| 银川市| 蚌埠市| 普定县| 志丹县| 神池县| 广灵县| 响水县| 淅川县| 衡阳市| 都兰县| 尼勒克县| 黑山县| 凌海市| 井冈山市| 汕尾市| 宁津县| 石台县| 普陀区| 彭阳县| 烟台市| 孟村| 华坪县|