采用Filter的方法解決HttpServletRequest.getParameter亂碼的問題


其實就是利用這么一個原理:

byte[] bytes = str.getBytes("iso-8859-1");
String result = new String(bytes, charset); // 將str轉化為你指定的charset encoding

這個filter解決了什么問題呢?其實就是解決了使用request.getParameter時的亂碼問題,比如說,你有一個a.html或者a.jsp發送request給servlet_b,servlet_b利用request.getParameter把參數提取出來,然后送到c.jsp去顯示,如果你的a.html/jsp使用的編碼是GB2312或者UTF-8之類的,那么在傳輸的過程中就可能出現亂碼(具體我就不描述了,你拿這個代碼去試試就知道亂碼到底出現在哪里)

在web.xml中關於這個filter的一個參數是enable,如果你想關閉這個filter,那么令enable為false即可

完整的代碼如下,先給出測試代碼(見http://www.cnblogs.com/qrlozte/p/3515171.html):

input_attribute.html

AttributeSetterServlet.java

  req.getParameter讀取attribute,然后req.setAttribute(attribute),接着跳轉到display_attribute.jsp

display_attribute.jsp

  request.getAttribute("attribute")提取attribute

------------------------------Filter代碼-------------------------------

I18nServletFilter:

  注意,由於編碼的filter是屬於最基本的的filter,所以在web.xml中一定要把編碼的filter放在靠前的位置,至少是要放在encoding-sensitive的filter和servlet之前

  此filter從web.xml中讀取參數charset,charset就是你希望使用的編碼參數,比如說GBK、UTF-8之類的

  這個filter使用了一個自定義的類叫做I18nHttpServletRequestWrapper,繼承自HttpServletRequestWrapperHttpServletRequestWrapper提供了實現HttpServletRequest的基本框架,client可以繼續extends(參考design pattern:adapter),所以I18nHttpServletRequestWrapper重寫了的getParameter()以及getParameterValues()方法,添加了encoding的代碼,確保了getParameter()和getParameterValues()返回的內容都是由charset參數指定的編碼。並且,這個filter一但被調用,那么chain.doFilter()就會把這里的wrapper給傳遞下去,從而確保在filter-chain后面的filter或者其他的servlet的編碼問題都得到解決。

 1 /**
 2  * If this filter is enabled, it will wrap the default
 3  * HttpServletRequest with I18nHttpServletRequestWrapper and 
 4  * pass it to filters/servlets following this filter in the chain,
 5  * the purpose is to add charset-conversion functionality to the
 6  * HttpServletRequest, so that when you invoke getParameter()
 7  * or getParameterValues(), the returned string is encoded in the
 8  * specified charset. The charset name is specified by the init-param
 9  * of this filter. 
10  * */
11 public class I18nServletFilter implements Filter {
12 
13     private FilterConfig filterConfig;
14 
15     private String charsetName = "UTF-8";
16     private boolean enable = false;
17     //Handle the passed-in FilterConfig
18     public void init(FilterConfig filterConfig) throws ServletException {
19         this.filterConfig = filterConfig;
20         String enableString = filterConfig.getInitParameter("enable");
21         if (enableString != null && enableString.equalsIgnoreCase("true")) {
22             this.enable = true;
23         }
24         String charset = filterConfig.getInitParameter("charset");
25         if (charset != null && charset.trim().length() != 0) {
26             this.charsetName = charset;
27         }
28     }
29 
30     //Process the request/response pair
31     public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
32         if (this.enable) {
33             try {
34                 if (this.charsetName != null) {
35                     // System.out.println(this + ", " + this.charsetName); // @Debug
36                     I18nHttpServletRequestWrapper requestWrapper = 
37                         new I18nHttpServletRequestWrapper( (HttpServletRequest) request, this.charsetName);
38                     // 注意:傳遞下去的是requestWrapper而不是request
39                     filterChain.doFilter(requestWrapper, response);
40                 } else {
41                     filterChain.doFilter(request, response);
42                 }
43             } catch(ServletException sx) {
44                 filterConfig.getServletContext().log(sx.getMessage());
45             } catch(IOException iox) {
46                 filterConfig.getServletContext().log(iox.getMessage());
47             }
48         } else {
49             filterChain.doFilter(request, response);
50         }
51     }
52 
53     // Clean up resources
54     public void destroy() {
55         filterConfig = null;
56         charsetName = null;
57     }
58     
59 }

HttpServletRequestWrapper

  上面已經解釋過了,這個是利用decorator pattern對HttpServletRequest進行了一次封裝,對getParameter()以及getParameterValues()方法增加編碼轉換的功能

 1 /**
 2  * This class wraps the default HttpServletRequest to provide
 3  * a charset-conversion functionality, so that getParameter()
 4  * and getParameterValues() can return parameter-strings encoded
 5  * in your specified charset. 
 6  * 
 7  * The charset is specified by the constructor
 8  * of this class.
 9  * */
10 public class I18nHttpServletRequestWrapper extends HttpServletRequestWrapper {
11     private Map<String, String[]> paramMap = new HashMap<String, String[]>();
12     private String charsetName = "iso-8859-1";
13     /**
14      * 每次I18nServletFilter.doFilter()被調用,就會新建一個I18nHttpServletRequestWrapper.
15      * 那么I18nHttpServletRequestWrapper就會提取出HttpServletRequest中的所有parameters並
16      * 存放到paramMap中.
17      * 
18      * 由於服務器跳轉是request范圍的,所以服務器跳轉始終是一個request,只會new一個I18nHttpServletRequestWrapper
19      * */
20     public I18nHttpServletRequestWrapper(HttpServletRequest request, String charsetName) {
21         super(request);
22         // System.out.println("constructing " + this); // @Debug
23         this.charsetName = charsetName;
24         initParameterMap(request);
25     }
26     private void initParameterMap(HttpServletRequest request) {
27         if (request == null) {
28             return;
29         }
30         Map<String,String[]> map = request.getParameterMap();
31         Set<String> names = map.keySet();
32         String[] values;
33         for (Iterator<String> i = names.iterator(); i.hasNext(); ) {
34             String name = i.next();
35             values = map.get(name);
36             for (int j = 0; j < values.length; j++) {
37                 values[j] = convertCharset(values[j]);
38             }
39             this.paramMap.put(name, values);
40         }
41     }
42     public String getParameter(String name) {
43         String[] values = this.getParameterValues(name);
44         if (values != null && values.length > 0) {
45             return values[0];
46         } else {
47             return null;
48         }
49     }
50     public String[] getParameterValues(String name) {
51         return this.paramMap.get(name);
52     }
53     private boolean isInParamValues(String s) {
54         for (String[] values : paramMap.values()) {
55             for (String value : values) {
56                 if (s.equals(value)) {
57                     return true;
58                 }
59             }
60         }
61         return false;
62     }
63     @Override
64     public void setAttribute(String name, Object o) {
65         /*
66          * 防止已經被編碼過的String被重新編碼、
67          * 比如說,一個String x本來是iso-8859-1,通過convertCharset轉碼成為了utf-8
68          * 然后,你又調用convertCharset(x),那么convertCharset就會首先把x解碼成
69          * iso-8859-1的byte(導致亂碼,因為此時x已經是utf-8編碼了),然后再用utf-8編碼(還是亂碼)
70          * 
71          * 那么已經被編碼過的String來源有哪些?
72          * 1、通過request.setAttribute已經添加的string attribute value
73          * 2、已經在paramMap中的value
74          * */
75         if (o instanceof String && !isInParamValues((String)o) && !o.equals(getAttribute(name))) {
76             // System.out.println("setAttr:check " + paramMap + "\n" + getAttribute(name)); // @Debug
77             o = convertCharset((String)o);
78         }
79         super.setAttribute(name, o);
80     }
81     
82     private String convertCharset(String str) {
83         if (str == null) {
84             return null;
85         }
86         try {
87             // System.out.println("before convert: " + str); // @Debug
88             str = new String(str.getBytes("iso-8859-1"), this.charsetName);
89             // System.out.println("\tafter convert: " + str); // @Debug
90             return str;
91         } catch (UnsupportedEncodingException ex) {
92             Logger.getLogger(this.getClass().toString()).log(Level.SEVERE, ex + ", charset = " + this.charsetName);
93         }
94         return null;
95     }
96 }

web.xml

 1     <!-- i18nservletfilter -->
 2     <filter>
 3         <filter-name>i18nservletfilter</filter-name>
 4         <filter-class>org.foo.filterdemo.I18nServletFilter</filter-class>
 5         <init-param>
 6             <param-name>enable</param-name>
 7             <param-value>true</param-value>
 8         </init-param>
 9         <init-param>
10             <param-name>charset</param-name>
11             <param-value>UTF-8</param-value>
12         </init-param>
13     </filter>
14     <filter-mapping>
15         <filter-name>i18nservletfilter</filter-name>
16         <url-pattern>/*</url-pattern>
17     </filter-mapping>

總結

  這個什么I18nServletFilter是我先前在翻閱資料的時候找到的,現搬過來吧,又解決不了編碼的問題

  自己搗鼓了半天吧,好歹算是能用了,但是總的感覺就是:Verbose + Error-prone.

  推薦使用http://www.cnblogs.com/qrlozte/p/3515171.html給出的方法


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM