其實就是利用這么一個原理:
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,繼承自HttpServletRequestWrapper,HttpServletRequestWrapper提供了實現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.