一次post提交中文形成亂碼問題的分析

前提條件 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解決傳參的編碼問題,這個主意非我所想,感謝團隊的小夥提供這個方法,我只是代勞整理了出來。

相關文章
相關標籤/搜索