前提條件 javascript
在解決問題以前,web模塊中配置了自定義的HttpEncodingFilter和GetHttpServletRequestWrapper,期待可以解決全部服務器上的中文亂碼問題,很遺憾,讓你們失望了。最後給出web.xml中過濾器和兩個類的明細。 html
在某個功能中,form表單經過post提交,表單中大部分參數經過struts1的form.vo(包含中文)封裝,另外兩個參數(包含中文)經過表單域直接提交,格式大概就是這樣子了: java
<form action="site.do" method="post"> <input name="vo.name" value="黃金沙" /> //注意跟strtus form關聯 <input name="authValue" value="系統管理員" /> </form>
這樣在SiteAction中,根據vo獲取name的值不會亂碼,經過request.getParameter("authValue")就可能出現亂碼(隨服務器而定),通過測試在jboss和weblogic下面,不出現亂碼,在was下面亂碼。 web
分析過程 apache
因爲代碼使用了框架struts1,表單提交時,vo.name通過struts的處理,authValue是不通過處理的,這也就形成應用服務器對這兩個參數的不一樣處理。 api
首先讓咱們看下HttpEncodingFilter這個類: 數組
public class HttpEncodingFilter implements Filter { private String charset = "UTF-8"; private String uriEncoding = "iso8859-1"; private FilterConfig config; private Logger logger = Logger.getLogger(this.getClass()); public void doFilter(javax.servlet.ServletRequest request, javax.servlet.ServletResponse response, javax.servlet.FilterChain chain) throws java.io.IOException, javax.servlet.ServletException { response.setCharacterEncoding(charset); // 新增長的代碼 (仍然存在問題,當使用multipart提交時,若是參數寫在表單中會出現中文亂碼) TODO HttpServletRequest req = (HttpServletRequest) request; boolean hasEncoding = false; if (req.getCharacterEncoding() != null) { hasEncoding = true; } if (!hasEncoding) { req = new GetHttpServletRequestWrapper(req, charset, uriEncoding); } chain.doFilter(req, response); }; public void init(FilterConfig config) throws ServletException { this.config = config; String charset = config.getInitParameter("charset"); if (charset != null && charset.trim().length() != 0) { this.charset = charset; } String uriEncoding = config.getInitParameter("uriEncoding"); if (uriEncoding != null && uriEncoding.trim().length() != 0) { this.uriEncoding = uriEncoding; } } public void destroy() { // TODO Auto-generated method stub } }
在這個過濾器中,沒有設置request的CharacterEncoding,設置了response的CharacterEncoding爲GBK,可是用自定義的GetHttpServletRequestWrapper封裝了ServletRequest,能夠看下這個類怎麼寫的: 瀏覽器
package com.excellence.exportal.base.common; import java.io.UnsupportedEncodingException; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import org.apache.log4j.Logger; public class GetHttpServletRequestWrapper extends HttpServletRequestWrapper { private String charset = "UTF-8"; private String uriEncoding = "iso8859-1"; private String Method = "GET"; private boolean isAjax = false; private Logger logger = Logger.getLogger(this.getClass()); public GetHttpServletRequestWrapper(HttpServletRequest request) { super(request); } /** * 得到被裝飾對象的引用和採用的字符編碼 * @param request * @param charset */ public GetHttpServletRequestWrapper(HttpServletRequest request, String charset,String uriEncoding) { super(request); this.charset = charset; this.uriEncoding = uriEncoding; } /** * 得到被裝飾對象的引用和採用的字符編碼 * @param request * @param charset */ public GetHttpServletRequestWrapper(HttpServletRequest request, String charset,String uriEncoding,String method,boolean isAjax) { super(request); this.charset = charset; this.uriEncoding = uriEncoding; this.Method = method; this.isAjax = isAjax; } /** * 實際上就是調用被包裝的請求對象的getParameter方法得到參數,而後再進行編碼轉換 */ @Override public String getParameter(String name) { String value = super.getParameter(name); value = (value == null ? null : convert(value)); return value; } @Override public String[] getParameterValues(String name){ String[] resultArr = super.getParameterValues(name); if(resultArr == null || resultArr.length <= 0){ return null; } for(int i=0;i<resultArr.length;i++){ String temp = resultArr[i]; if(temp!=null){ resultArr[i] = convert(temp); } } return resultArr; } @Override public Map getParameterMap() { //在高版本的api中 map的value是string數組,而低版本的value是string,故使用'?'號佔位 Map<String,?> paramMap = super.getParameterMap(); Object value = null; for(Map.Entry entry:paramMap.entrySet()){ value = entry.getValue(); if(value!=null){ if(value instanceof String[]){ String[] values = (String[])value; for(int i=0;i<values.length;i++){ values[i] = convert(values[i]); } entry.setValue(values); }else{ entry.setValue(convert(value.toString())); } } } return paramMap; } public String convert(String target) { try { byte bytes[] = target.getBytes(this.uriEncoding); String result = new String(bytes,this.charset); return result; } catch (UnsupportedEncodingException e) { return target; } } }
在這個類中,重寫了getParameter、getParameterValues、getParameterMap這些方法,在這些方法裏面都實現了將value按指定編碼轉碼的邏輯。 服務器
繼續回到表單提交的狀況,上文提到了struts的表單提交與普通表單域處理中文時,不一樣表現(亂碼和不亂嗎),這塊也反覆提到跟應用服務器對請求參數的處理分不開,結合GetHttpServletRequestWrapper來說。 app
其中struts中表單提交在jboss、was、weblogic中的表現一致,均不會出現亂碼,具體緣由的話,struts將瀏覽器端提交的參數,不經轉碼直接交給GetHttpServletRequestWrapper的getParameterValues方法處理,其中post提交的時候,在頁面已經制定contextType是GBK的編碼,這樣getParameterValues拿到編碼後的值,通過一層轉碼,獲得正常的中文值,因此用struts提交始終不出現亂碼的狀況(前提post方式提交表單)。
可是authValue的值獲取就不同了,在action中經過getParameter API獲取相應的值,因爲GetHttpServletRequestWrapper封裝了實際的request,這裏調用的是GetHttpServletRequestWrapper中的getParameter方法,細細看這個方法發現,裏面會對拿到的value進行一層轉碼。問題就出來了,若是轉碼後出現亂碼,只能說明轉碼前的內容不是提交後瀏覽器編碼的內容(有點繞口)。這裏須要解釋一個常識,post提交方式,瀏覽器會根據jsp頁面的contextType的編碼對錶單值進行編碼。通過進一步的對比分析,發如今was(8.5版本)下面運行這段代碼,出現中文亂碼,在weblogic(10.3版本)下面運行,正常。
簡單描述這個問題:在瀏覽器提交post請求後,對於getParameter的API,weblogic不會根據處理編碼;而was則會進行一次轉碼(多是根據jvm的編碼進行轉碼),這樣GetHttpServletRequestWrapper的getParameter API必然會出現亂碼了。
解決辦法
一、針對應用服務器給出不一樣的解決方案,在代碼層面作適配,缺點,過濾器顯得不夠通用。
二、從根本上解決亂碼問題,不傳中文,實在要傳中文的地方,在提交請求前,將中文進行轉碼,經常使用的轉碼方案,利用javascript的encodeURIComponent編碼,根據請求類型get 或者 post 屢次使用encodeURIComponent。
這裏給出咱們用的一種解決方案,將中文用base64編碼,提交後,在後臺用base64解碼,這樣的好處,能夠避免瀏覽器對錶單值就行默認的編碼處理。可是注意一個問題,注意base64解碼時的程序的健壯性處理,由於某些狀況寫,因爲解碼出現異常,這是正常的。
參考文檔
http://www.cnblogs.com/gywbg/archive/2012/04/13/2445634.html
http://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/
利用base64解決傳參的編碼問題,這個主意非我所想,感謝團隊的小夥提供這個方法,我只是代勞整理了出來。