一次post提交中文造成乱码问题的分析

前提条件

在解决问题之前,web模块中配置了自定义的HttpEncodingFilter和GetHttpServletRequestWrapper,期待能够解决所有服务器上的中文乱码问题,很遗憾,让大家失望了。最后给出web.xml中过滤器和两个类的明细

在某个功能中,form表单通过post提交,表单中大部分参数通过struts1的form.vo(包含中文)封装,另外两个参数(包含中文)通过表单域直接提交,格式大概就是这样子了:

<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下面乱码。

分析过程

由于代码使用了框架struts1,表单提交时,vo.name经过struts的处理,authValue是不经过处理的,这也就造成应用服务器对这两个参数的不同处理。

首先让我们看下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来讲。

其中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必然会出现乱码了。

解决办法

1、针对应用服务器给出不同的解决方案,在代码层面做适配,缺点,过滤器显得不够通用。

2、从根本上解决乱码问题,不传中文,实在要传中文的地方,在提交请求前,将中文进行转码,常用的转码方案,利用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解决传参的编码问题,这个主意非我所想,感谢团队的小伙提供这个方法,我只是代劳整理了出来。

时间: 2024-11-08 18:14:24

一次post提交中文造成乱码问题的分析的相关文章

提交中文数据乱码问题总结

提交中文数据乱码问题总结 1.如果提交方式为post,想不乱码,只需要在服务器端设置request对象的编码即可,客户端以哪种编码提交的,服务器端的request对象就以对应的编码接收,比如客户端是以UTF-8编码提交的,那么服务器端request对象就以UTF-8编码接收(request.setCharacterEncoding("UTF-8")) 2.如果提交方式为get,设置request对象的编码是无效的,request对象还是以默认的ISO8859-1编码接收数据,因此要想不

有道翻译接口Api 直接提交中文返回乱码

有道翻译接口Api http://fanyi.youdao.com/openapi.do?keyfrom=***&key=***&type=data&doctype=json&version=1.1&q=word word就是查询内容,如hello,但是查询中文“你好”出现返回乱码. 解决办法:String lastedword=URLEncoder.encode(word, "UTF-8"); http://fanyi.youdao.com/o

表单提交中文后台乱码

通过form表单提交到后台 通过form传入后台,提交方式分为post和get两种.两者的区别需要通过提交表单后才看得出来,主要是在数据发送方式和接收方式上.Post和Get都是表单属性Method的可选值,Method的默认值为Get,两者的主要区别在于: 1.在客户端,Get方式在通过URL提交数据,提交后在地址栏中的地址会出现传入到后台的参数:而Post提交后地址栏中的地址不会出现参数. 2.在服务器端只能用Request.QueryString来获取Get方式提交来的数据,用Post方式

提交中文数据乱码问题---web.xml

前端时间,做了个纯springmvc框架的一个后台系统,遇到了不少问题.特别是编码问题,让我纠结了很久.每次ajax传入数据的时候需要将form中的数据先进行编码 encodeURI(AA); 利用ajax传入后controll层 接受后需再转码 java.net.URLDecoder.decode(param, "UTF-8"); 才能将中文字符写到数据库里. 这样form中有中文的地方都需要这样做一下,突然觉得好麻烦.....可是我之前没记得传个数据这么麻烦啊.就在网上找啊找. 终

pymssql读取varchar字段中文显示乱码的问题分析

问题 用python的pymssql模块读取旧业务系统后台SQL Server 2000数据库展示数据为乱码 开发环境 操作系统:windows 8 数据库 MS SQL Server 2000,默认配置 python 2.7.6 pymssql 2.1.1 开发工具:PyCharm 4.0 业务逻辑 数据库的[rooms]表记录一些功能房间列表,与其他接口数据进行对比,然后输出对比结果. rooms表结构: CREATE TABLE [rooms] ( [id] [int] IDENTITY

JQuery AJAX提交中文乱码的解决方案

?JQuery是一个非常优秀的框架,在特定场合下使用JQuery提交数据,相当的方便快捷. 但是,在处理一个GB2312编码的网站AJAX提交时,中文数据却成了乱码. 现象如下: 1)在Firefox下,处理页面的编码为gb2312,提交数据没有问题,中文能够正确解析: 2)在IE8下,处理页面的编码为gb2312,提交中文数据出现乱码. 无论是$.post还是$.ajax,抑或$.ajaxSubmit(来自于Form插件),在之前的UTF-8编码的网站都没有出现过任何问题, 看来是由于提交数据

在jQuery中Ajax的Post提交中文乱码的解决方案(转)

引言: 在jQuery的Ajax POST请求中,进行请求,其中的中文在后台,显示为乱码,该如何解决呢? 问题的引入: var regid = $('#oregion').combobox('getValue'); //var sname = $('#sname').val(); var sname = encodeURI($('#sname').val(),"UTF-8"); if(regid!=""&&regid!='undefined'){ $

get提交时中文传值乱码的有关问题

get提交时中文传值乱码的问题 get提交时中文传值乱码的问题 url=curWarnList.action paramBean.bsIndex=1&paramBean.siteName=萧山A基站,href连接提交其实是get提交方式,会造成中文乱码.这个主要是编码方式不统一.JSP(手动设置).Java(字符串是unicode编码).操作系统..数据库().Tomcat等等,各种不同环境介质都有不同的编码方式,如果编码方式不统一,就会造成乱码. url参数中文是以ISO8859-1的编码方式

表单提交中文时出现乱码

下面是一个提交页面(submit.jsp),代码如下: <html> <head> <title>JSP的中文处理</title> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> </head> <body> <form name="form1" method=&quo