解決表單提交參數亂碼問題

解決表單提交參數亂碼問題【終極版】不看後悔

提交表單亂碼問題,一直是困然網站開發人員的“吐血”問題,這問題雖說不痛不癢,但是絕對“噁心人”。之前自己遇到這個問題是,一直是能繞過就繞過,懶得理它,直到今天我又遇到它,我知道,我必須得治治它了。

表單提交通常有兩種方式,一種是GET方式,一種時POST方式,兩種方式這裏就不詳細解釋了;然後表單參數的傳遞,也有兩種方式,一種是直接把參數加在URL上,以key=value的方式傳遞,一種是在表單內部添加帶name屬性的標籤,例如inputselect標籤等。那麼它們組合在一起,就有4種方式:


URL傳參

表單標籤傳參

混合傳參

GET

A

B

C

POST

D

E

F

先說一下使用中會出現的問題。AC方式中,URL上的參數會被表單的參數沖掉,所以AC方式不要使用。

在說說這幾種方式的特點,在GET方式中,表單中所有的參數實際上都是被追加到URL上的(這也是get方式的url傳參,url參數被沖掉的原因),表單最後提交給服務器的就是一個urlurl長度一般限制爲255字符)。這種方式產生的亂碼最難纏。

POST方式中,如果參數位於表單中(等同於ajax提交數據時的data內容),參數是以非url形式提交的,所以這種通常不會出現亂碼,而且也容易解決。如果參數位於url中,那參數的傳遞方式和get方式是一樣的,這時產生亂碼的原因和get方式是一樣的。

現在我們把問題抽象出來了,參數傳遞有兩種,一種是通過url傳參,一種是通過data傳參。

亂碼之所以亂碼,是因爲編碼和解碼的格式不一致。

說說我們通常解決亂碼的方法。通常有兩類解決辦法,一類是對參數進行編碼,然後後臺進行解碼,這種方式對於以上幾種傳參都適用,但是因爲前臺要編碼,後臺需要解碼,所以增加了代碼複雜性。另一種方式就是弄個filterspring自帶一個,就是這貨org.springframework.web.filter.CharacterEncodingFilter,可以直接把它配在web.xml裏面),對所有請求都setCharactorEncoding()UTF-8,這種方式通常都行。之所以說通常都行,是因爲這種方式之對通過data方式傳遞的參數有效,對於通過url傳遞的參數無效,這也是爲什麼get提交方式產生亂碼概率大的原因。

但是我們怎麼通過url傳參時的亂碼呢?也許有人會說,不用url傳參不就可以了,但是在許多情況下,我不得不使用url傳參,比如一個超鏈接。

 

其實只要找到問題所在,解決方案也就好辦了。開始時,我的辦法是寫一個filter,對於通過get方式提交的參數,把所有的參數都進行一下編碼轉換:ISO-8859-1 à UTF-8。這種方式我使用了很長時間,直到有一次,我不得不使用post方式的混合傳參時,才發現url上的參數居然被認爲是post方式傳遞的,當然也沒有被我的filter攔截,當然也就亂碼了。

 

不過既然要死磕,就一定要把這問題解決。

思路倒是很清晰,雖然是post方式提交的,但是我們只需要把其中url方式傳參的參數進行轉碼即可(data傳參只需要設置CharactorEncoding即可,如果轉碼那就轉成亂碼了),可是怎麼知道哪些參數是url傳遞呢?

HttpServletRequest對象有getQueryString()這個方法,這個方法能夠獲得url傳遞的參數的字符串,當然了,參數也就包含在其中。所以我們只要把其中的參數名分離出來即可,這些就是我們需要進行轉碼的,別的不需要解碼。

 

在然後呢,我們通過request獲取參數時,一般會通過這麼幾個方法:getParameter()getPrarmeterMap()getParameterValues()這三個方法。所以我們只要在這三個方法上“做手腳”即可。另外,如果某些參數是按照傳引用(相對於傳值而言,瞭解c的人,對這個應該比較瞭解。另外雖然java本質上都是傳值,但是如果對象不是基本類型時,就會有傳引用的效果)傳遞的,我們還要設置一些標誌位,防止多次轉碼。

 

思路已經清楚了,下面直接貼本人的成型代碼。

第一個是GetHttpServletRequestWrapper,這個類是“主角”,完成對參數的篩選和轉碼:


[java] view plain copy print?

  1. /* 

  2.  * Copyright (c) 2014, ShiXiaoyong. All rights reserved. 

  3.  */  

  4. package com.common.filter;  

  5.   

  6. import java.io.UnsupportedEncodingException;  

  7. import java.util.Enumeration;  

  8. import java.util.HashMap;  

  9. import java.util.Map;  

  10.   

  11. import javax.servlet.http.HttpServletRequest;  

  12. import javax.servlet.http.HttpServletRequestWrapper;  

  13.   

  14. /** 

  15.  * 描述:GetHttpServletRequestWrapper 

  16.  *  

  17.  * <pre> 

  18.  * HISTORY  

  19.  * ****************************************************************  

  20.  *  ID   DATE           PERSON          REASON  

  21.  * 1     2015-3-6       Shixy           Create  

  22.  * 2     2015-3-23      Shixy           增加對post混合傳參方式的支持  

  23.  * **************************************************************** 

  24.  * </pre> 

  25.  *  

  26.  * @author Shixy 

  27.  * @since 1.0 

  28.  */  

  29. public class GetHttpServletRequestWrapper extends HttpServletRequestWrapper {  

  30.   

  31.     private String charset = "UTF-8";  

  32.   

  33.     private static final String ENCODED = "__encoded";  

  34.   

  35.     private Map<String, String> urlParamNames = null;  

  36.   

  37.     /** 

  38.      * @param request 

  39.      */  

  40.     public GetHttpServletRequestWrapper(HttpServletRequest request) {  

  41.         super(request);  

  42.         initUrlParameterNames();  

  43.     }  

  44.   

  45.     /** 

  46.      * 獲得被裝飾對象的引用和採用的字符編碼 

  47.      *  

  48.      * @param request 

  49.      * @param charset 

  50.      */  

  51.     public GetHttpServletRequestWrapper(HttpServletRequest request, String charset) {  

  52.         super(request);  

  53.         this.charset = charset;  

  54.         initUrlParameterNames();  

  55.     }  

  56.   

  57.     @Override  

  58.     public Enumeration<String> getParameterNames() {  

  59.         return super.getParameterNames();  

  60.     }  

  61.   

  62.     /** 

  63.      * 實際上就是調用被包裝的請求對象的getParameter方法獲得參數,然後再進行編碼轉換 

  64.      */  

  65.     @Override  

  66.     public String getParameter(String name) {  

  67.         String value = super.getParameter(name);  

  68.         // 根據urlParamNames是否包含此值來判斷是否需要對其進行get方式轉碼  

  69.         if (!urlParamNames.containsKey(name)) {  

  70.             return value;  

  71.         }  

  72.         if (null != value) {  

  73.             value = convert(value);  

  74.         }  

  75.         return value;  

  76.     }  

  77.   

  78.     @Override  

  79.     public String[] getParameterValues(String name) {  

  80.         // values也是傳值  

  81.         String[] values = super.getParameterValues(name);  

  82.         if ((!urlParamNames.containsKey(name))) {  

  83.             return values;  

  84.         }  

  85.         for (int i = 0; i < values.length; i++) {  

  86.             values[i] = convert(values[i]);  

  87.         }  

  88.   

  89.         return values;  

  90.     }  

  91.   

  92.     @Override  

  93.     public Map<String, String[]> getParameterMap() {  

  94.         Map<String, String[]> map = super.getParameterMap();  

  95.         // 是否已經轉碼的標識位  

  96.         // 因爲map是傳引用的,因此多次調用時,原值會被轉碼轉碼在轉碼,因此要設置此標誌位,防止多次轉碼  

  97.         if ("1".equals(this.getAttribute(ENCODED))) {  

  98.             return map;  

  99.         }  

  100.   

  101.         // 對map中所有的url傳參進行編碼  

  102.         // 遍歷map中的參數,轉換器編碼  

  103.         for (String key : urlParamNames.keySet()) {  

  104.             String[] value = map.get(key);  

  105.             if (value != null) {  

  106.                 for (int i = 0; i < value.length; i++) {  

  107.                     value[i] = convert(value[i]);  

  108.                 }  

  109.             }  

  110.         }  

  111.   

  112.         this.setAttribute(ENCODED, "1");  

  113.         return map;  

  114.     }  

  115.   

  116.     /** 

  117.      * 將字符串轉碼 

  118.      * ISO-8859-1爲國際通用url編碼 

  119.      * @param target 

  120.      * @return 

  121.      */  

  122.     private String convert(String target) {  

  123.         try {  

  124.             return new String(target.trim().getBytes("ISO-8859-1"), charset);  

  125.         } catch (UnsupportedEncodingException e) {  

  126.             return target;  

  127.         }  

  128.     }  

  129.       

  130.     /** 

  131.      * 初始化設置url傳值的參數名 

  132.      */  

  133.     private void initUrlParameterNames() {  

  134.         if (null != urlParamNames) {  

  135.             return;  

  136.         }  

  137.         // 獲取所有的url傳參的參數名  

  138.         urlParamNames = new HashMap<String, String>();  

  139.         String st = this.getQueryString();  

  140.         if (null == st || 0 == st.length()) {  

  141.             return;  

  142.         }  

  143.         String[] params = this.getQueryString().split("&");  

  144.         for (String p : params) {  

  145.             if (!p.contains("=")) {  

  146.                 continue;  

  147.             }  

  148.             urlParamNames.put(p.substring(0, p.indexOf("=")), null);  

  149.         }  

  150.     }  

  151.   

  152. }  

/*  * Copyright (c) 2014, ShiXiaoyong. All rights reserved.  */ package com.common.filter; import java.io.UnsupportedEncodingException; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; /**  * 描述:GetHttpServletRequestWrapper  *   * <pre>  * HISTORY   * ****************************************************************   *  ID   DATE           PERSON          REASON   * 1  2015-3-6 Shixy Create   * 2  2015-3-23 Shixy 增加對post混合傳參方式的支持   * ****************************************************************  * </pre>  *   * @author Shixy  * @since 1.0  */ public class GetHttpServletRequestWrapper extends HttpServletRequestWrapper { private String charset = "UTF-8"; private static final String ENCODED = "__encoded"; private Map<String, String> urlParamNames = null; /**  * @param request  */ public GetHttpServletRequestWrapper(HttpServletRequest request) { super(request); initUrlParameterNames(); } /**  * 獲得被裝飾對象的引用和採用的字符編碼  *   * @param request  * @param charset  */ public GetHttpServletRequestWrapper(HttpServletRequest request, String charset) { super(request); this.charset = charset; initUrlParameterNames(); } @Override public Enumeration<String> getParameterNames() { return super.getParameterNames(); } /**  * 實際上就是調用被包裝的請求對象的getParameter方法獲得參數,然後再進行編碼轉換  */ @Override public String getParameter(String name) { String value = super.getParameter(name); // 根據urlParamNames是否包含此值來判斷是否需要對其進行get方式轉碼 if (!urlParamNames.containsKey(name)) { return value; } if (null != value) { value = convert(value); } return value; } @Override public String[] getParameterValues(String name) { // values也是傳值 String[] values = super.getParameterValues(name); if ((!urlParamNames.containsKey(name))) { return values; } for (int i = 0; i < values.length; i++) { values[i] = convert(values[i]); } return values; } @Override public Map<String, String[]> getParameterMap() { Map<String, String[]> map = super.getParameterMap(); // 是否已經轉碼的標識位 // 因爲map是傳引用的,因此多次調用時,原值會被轉碼轉碼在轉碼,因此要設置此標誌位,防止多次轉碼 if ("1".equals(this.getAttribute(ENCODED))) { return map; } // 對map中所有的url傳參進行編碼 // 遍歷map中的參數,轉換器編碼 for (String key : urlParamNames.keySet()) { String[] value = map.get(key); if (value != null) { for (int i = 0; i < value.length; i++) { value[i] = convert(value[i]); } } } this.setAttribute(ENCODED, "1"); return map; } /**  * 將字符串轉碼  * ISO-8859-1爲國際通用url編碼  * @param target  * @return  */ private String convert(String target) { try { return new String(target.trim().getBytes("ISO-8859-1"), charset); } catch (UnsupportedEncodingException e) { return target; } } /**  * 初始化設置url傳值的參數名  */ private void initUrlParameterNames() { if (null != urlParamNames) { return; } // 獲取所有的url傳參的參數名 urlParamNames = new HashMap<String, String>(); String st = this.getQueryString(); if (null == st || 0 == st.length()) { return; } String[] params = this.getQueryString().split("&"); for (String p : params) { if (!p.contains("=")) { continue; } urlParamNames.put(p.substring(0, p.indexOf("=")), null); } } }




第二個就是一個簡單的filter,用於使用上面的RquestWrapper轉碼我們的參數:


[java] view plain copy print?

  1. /*   

  2.  * Copyright (c) 2014, ShiXiaoyong. All rights reserved.  

  3.  */  

  4. package com.common.filter;  

  5.   

  6. import java.io.IOException;  

  7.   

  8. import javax.servlet.Filter;  

  9. import javax.servlet.FilterChain;  

  10. import javax.servlet.FilterConfig;  

  11. import javax.servlet.ServletException;  

  12. import javax.servlet.ServletRequest;  

  13. import javax.servlet.ServletResponse;  

  14. import javax.servlet.http.HttpServletRequest;  

  15.   

  16. /**  

  17.  * 描述:GetMethodEncodingFilter  

  18.  *     針對GET方式提交的表單,進行編碼轉換 

  19.  * <pre>  

  20.  * HISTORY  

  21.  * ****************************************************************  

  22.  *  ID   DATE           PERSON          REASON  

  23.  *  1    2015-3-6       Shixy           Create  

  24.  * ****************************************************************  

  25.  * </pre>  

  26.  *   

  27.  * @author Shixy  

  28.  * @since 1.0  

  29.  */  

  30. public class GetMethodEncodingFilter implements Filter {  

  31.   

  32.     private String charset = "utf-8";  

  33.       

  34.     @Override  

  35.     public void destroy() {  

  36.     }  

  37.   

  38.     @Override  

  39.     public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {  

  40.           

  41.         HttpServletRequest req = (HttpServletRequest)request;    

  42.   

  43.         req = new GetHttpServletRequestWrapper(req,charset);    

  44.         filterChain.doFilter(req, response);    

  45.     }  

  46.   

  47.     @Override  

  48.     public void init(FilterConfig filterConfig) throws ServletException {  

  49.     }  

  50. }  

/*    * Copyright (c) 2014, ShiXiaoyong. All rights reserved.   */ package com.common.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; import javax.servlet.http.HttpServletRequest; /**   * 描述:GetMethodEncodingFilter   *     針對GET方式提交的表單,進行編碼轉換  * <pre>   * HISTORY   * ****************************************************************   *  ID   DATE           PERSON          REASON   * 1  2015-3-6 Shixy Create   * ****************************************************************   * </pre>   *    * @author Shixy   * @since 1.0   */ public class GetMethodEncodingFilter implements Filter { private String charset = "utf-8"; @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {              HttpServletRequest req = (HttpServletRequest)request;       req = new GetHttpServletRequestWrapper(req,charset);       filterChain.doFilter(req, response);   } @Override public void init(FilterConfig filterConfig) throws ServletException { } }




最後把我們的filter配置在web.xml裏即可,要注意順序,最佳位置是setCharatorEncoding那個filter後面。


[html] view plain copy print?

  1. <!-- get method url encode -->  

  2. <filter>  

  3.     <filter-name>getMethodEncodingFilter</filter-name>  

  4.     <filter-class>com.common.filter.GetMethodEncodingFilter</filter-class>  

  5. </filter>  

  6. <filter-mapping>  

  7.     <filter-name>encodingFilter</filter-name>  

  8.     <url-pattern>/*</url-pattern>  

  9. </filter-mapping>  


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章