LinkinPark的博客

当你的才华撑不起你的野心时,请心无旁骛,继续学习,愿多年以后你我都能成为自己想象的模样。...

Servlet--j2e中文乱码解决

我们在写项目的时候经常会传递一些中文参数,但是j2e默认使用ISO-8859-1来编码和解码,所以很容易出现中文乱码问题。这里我做一个统一的整理,其实这里的中文乱码问题和上一篇的路径问题都是j2e经常遇见的很普遍的问题,不管你使用不使用框架都是很容易发生的,所以好好的整理一下还是很有必要的。
  • 具体有可能发生乱码的地方有:
1. 从数据库到Java程序 byte——〉char
2. 从Java程序到数据库 char——〉byte
3. 从文件到Java程序 byte——〉char
4. 从Java程序到文件 char——〉byte
5. 从流到Java程序byte——〉char
6. 从Java程序到流char——〉byte
7. 从Java程序到页面显示 char——〉byte
8. 从页面form提交数据到Java程序byte——〉char

其他的暂时先不管,现在我们先来处理Servlet中的中文乱码,也就是上面最后2点。

  • 首先必须要明白的,Tomcat的参数问题无论是GET或是POST方式都是用8859_1编码的。
1,GET方式要看tomcat下源码,
protected static Locale defaultLocale = Locale.getDefault();貌似这里的编码使用的本地的编码,但是我们仔细看下
org.apache.tomcat.service.http. HttpRequestAdapter类
---- line=new String(buf, 0, count, Constants.CharacterEncoding.Default);
----  Constants.CharacterEncoding.Default=8859_1  
这段代码不好跟踪,千万不要被一些假象迷惑住,HttpRequestAdapter是从RequestImpl中派生的。但是,实际上用8080端口的Server并没有直接用到RequestImpl,而是用了HttpRequestAdapter来获得queryString。

2,POST方式我们看下javax.servlet.http.HttpUtils的parsePostData方法,下面贴出源码。

public static Hashtable parsePostData(int len, ServletInputStream in)
	{
		if (len <= 0)
		{
			return new Hashtable();
		}
		if (in == null)
		{
			throw new IllegalArgumentException();
		}

		byte[] postedBytes = new byte[len];
		try
		{
			int offset = 0;
			do
			{
				int inputLen = in.read(postedBytes, offset, len - offset);
				if (inputLen <= 0)
				{
					String msg = lStrings.getString("err.io.short_read");
					throw new IllegalArgumentException(msg);
				}
				offset += inputLen;
			}
			while (len - offset > 0);
		}
		catch (IOException e)
		{
			throw new IllegalArgumentException(e.getMessage());
		}

		try
		{
			String postedBody = new String(postedBytes, 0, len, "8859_1");
			return parseQueryString(postedBody);
		}
		catch (UnsupportedEncodingException e)
		{
		}
		throw new IllegalArgumentException(e.getMessage());
	}
其实研究这个tomcat的编码源码没啥意义,也就不用管了,记住就好,不管是GET还是POST,tomcat都是用8859_1来编码的。
  • OK,现在开始整理中文乱码的处理,解决Servlet中的乱码问题。
1,表单提交
POST方式,在代码第一行设置request的编码格式就OK。jsp页面中一般都是设置过编码格式的,一般都是UTF-8,所以我们在这里request也设置用UTF-8解码就OK。
req.setCharacterEncoding("UTF-8");
GET方式,上面的操作不生效,因为get方式提交参数丫的不是在header里面是在url后面跟着的,只能自己用String来转换了。
String userName = new String(req.getParameter("userName").getBytes("ISO-8859-1"),"UTF-8");

2,超链接和重定向
比如:
<a href="/linkin/LinkinServlet?userName=林肯公园">GET方式传参</a>
这2种情况和上面的用GET方式提交表单一样,处理方式也一样,这里不做赘述了。

3,上面的情况都是属于编码级别的,一般的我们的项目上了生产上不管是GET方式还是POST方式,编码格式这些都设置好了,最多是ajxa异步请求的时候增加过滤器编码设置。这里针对tomcat本地开发说下:
如果是GET方式,在修改tomcat配置的地方添加一个属性:URIEncoding,将该属性值设置为UTF-8,即可让Tomcat(默认ISO-8859-1编码)以UTF-8的编码处理get请求。
<Connector port="8080"   protocol="HTTP/1.1"      connectionTimeout="20000"    redirectPort="8443" URIEncoding="UTF-8" />
如果是POST方式,就只能增加编码过滤器。没使用框架的话用自己写一个过滤器,建议将过滤的编码写成配置的,比如写在<init-param>标签中。如果使用了spring,则直接配置就OK。
<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<!-- 编码格式 -->
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<!-- 控制是否强制设置编码,如果是true,不管request中有没有指定编码,这个过滤器设置编码都会被触发,如果是false,只是在request中没有设置编码的时候被触发设置上这里的编码 -->
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<!-- 这里是jsp中的编码格式 都被设置为统一的了 -->
	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping> 

下面贴出这个类的源码:核心就是doFilterInternal()方法。

package org.springframework.web.filter;

import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CharacterEncodingFilter extends OncePerRequestFilter
{

	/**
	 * @param request
	 * @param response
	 * @param filterChain
	 * @throws ServletException
	 * @throws IOException
	 * 个人不喜欢forceEncoding这种控制和别的控制一起写的写法,单独拎出来写成旗标多好
	 * 一般情况下,为了不冲掉自己的request中原有的编码,建议forceEncoding配置成false
	 */
	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException
	{
		//1,如果配置的encoding为空,不设置编码
		//2,如果配置的encoding不为空,forceEncoding为true,不管request中有没有自己的编码都会设置编码
		//3,如果配置的encoding不为空,forceEncoding为false,只有在request没有自己的编码的时候才会设置编码
		if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null))
		{
			request.setCharacterEncoding(this.encoding);
			if (this.forceEncoding)
			{
				response.setCharacterEncoding(this.encoding);
			}
		}
		filterChain.doFilter(request, response);
	}

}

另外这里也稍微花点时间来说明一下,OncePerRequestFilter这个抽象过滤器很好的实现了对每个request只执行一次过滤操作,如果有类似的需求可以继承该类并实现doFilterInternal方法来完成,上面的CharacterEncodingFilter就是继承这个抽象类的,下面贴出这个抽象类的源码,后面整理框架的时候我会做详细的整理的。


package org.springframework.web.filter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public abstract class OncePerRequestFilter extends GenericFilterBean {

	
	public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED";


	//开始过滤,有点小技巧,实现了只过滤一次的功能
	public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
			throw new ServletException("OncePerRequestFilter just supports HTTP requests");
		}
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;

		String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
		if (request.getAttribute(alreadyFilteredAttributeName) != null || shouldNotFilter(httpRequest)) {//第2次进来
			// Proceed without invoking this filter...
			filterChain.doFilter(request, response);
		}
		else {//第一次进来
			// Do invoke this filter...
			request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
			try {
				doFilterInternal(httpRequest, httpResponse, filterChain);
			}
			finally {//最后清空alreadyFilteredAttributeName属性
				// Remove the "already filtered" request attribute for this request.
				request.removeAttribute(alreadyFilteredAttributeName);
			}
		}
	}
	
	protected String getAlreadyFilteredAttributeName() {
		String name = getFilterName();
		if (name == null) {
			name = getClass().getName();
		}
		return name + ALREADY_FILTERED_SUFFIX;
	}
	
	protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
		return false;
	}
	
	//推迟到子类实现,真正的过滤的方法
	protected abstract void doFilterInternal(
			HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException;

}

4,向页面传参
后台处理完逻辑后,跳转到页面上,如果页面上中文出现乱码,比如
resp.getWriter().write(req.getParameter("userName"));
在返回响应之前添加
response.setCharacterEncoding("UTF-8");
其实用上面那个设置编码格式的方式不怎么好,最好用下面这种的。
response.setContentType("text/html;charset=UTF-8");
response.setContentType("application/json;charset=gbk");


5,最后2点补充。
第一点,常用中文字符用utf-8编码占用3个字节,用GBK、GB2312编码的汉字占2个字节,严格地用iso8859-1无法表示汉字,只能转为问号。所以当我们传递的中文如果是基数的时候,即使我们正常编码和转码了也会出现乱码,在IE6版本一下就会出现这种情况。解决的办法就是在前台页面就编码下中文;
java.net.URLEncoder.encode("林肯公园","UTF-8");
在后台不需要转码,Servlet引擎已经帮我做好了。某些情况下比如搜索引擎的搜索时,如果发送请求的是GET方式,浏览器上面的中文就会变成application/x-www-form-urlencoded MIME字符串,比如“%E6%E6”这种,Servlet引擎来解析这段字符串的时候自动会给我们转会成汉字的。转码的代码如下:
URLDecoder.decode(req.getParameter("userName"), "UTF-8");

第二点,BASE64Encoder,BASE64Decoder编码和解码,这种编码和解码还会涉及算法的,了解下好了。这2个类在API中查不到,因为JDK已经不推荐使用了,不过我个人觉得还是挺好使的。

String name = new sun.misc.BASE64Encoder().encode("林肯公园".getBytes());// name:wda/z7mr1LA=
		System.out.println(new String((new sun.misc.BASE64Decoder()).decodeBuffer(name)));//林肯公园


OK,最后贴出自己写的Servlet和jsp:

package linkin;

import java.io.IOException;
import java.net.URLDecoder;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author LinkinPark
 * @author 2015-7-10
 * @Descri 解决j2e中文乱码问题
 */
public class LinkinServlet extends HttpServlet
{
	private static final long serialVersionUID = 1L;

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
	{
		//req.setCharacterEncoding("UTF-8");//解决POST方式
		//String userName = new String(req.getParameter("userName").getBytes("ISO-8859-1"),"UTF-8");//解决GET方式
		System.out.println(req.getParameter("userName"));
		System.out.println(URLDecoder.decode(req.getParameter("userName"), "UTF-8"));
		req.setAttribute("userName", "林肯公园");
		//resp.setCharacterEncoding("UTF-8");//解决向页面传参乱码问题,建议使用下面这种
		resp.setContentType("text/html;charset=UTF-8");//解决向页面传参乱码问题,建议使用这种
		resp.getWriter().write(req.getParameter("userName"));
		//req.getRequestDispatcher("/jsp/Linkin1.jsp").forward(req, resp);
		//resp.sendRedirect("/linkin/jsp/Linkin1.jsp");

		//下面使用base64来编码和解码
		String name = new sun.misc.BASE64Encoder().encode("林肯公园".getBytes());// name:wda/z7mr1LA=
		System.out.println(new String((new sun.misc.BASE64Decoder()).decodeBuffer(name)));//林肯公园
		
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
	{
		this.doGet(req, resp);
	}
	
	
	public static void main(String[] args) throws Exception
	{
		
	}

}
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">

<title>Servlet中文乱码</title>
<script type="text/javascript" src="/linkin/jsp/jquery-1.8.0.js"></script>
<script type="text/javascript">
var huhu = function(){
	var userName = $("#userName").val();
	$.ajax({
        url : "/linkin/LinkinServlet",
        type : "GET",
        async : true,
        data:{userName:userName},
        dataType : "json",
        success : function(a) {
        }
    });
}

</script>

</head>

<body>
	<form action="/linkin/LinkinServlet" method="GET">
		姓名:<input type="text" name="userName" id="userName" />
		<input type="button" value="提交" name="tijiao" onclick="huhu();"/>
		<a href="/linkin/LinkinServlet?userName=<%=java.net.URLEncoder.encode("林肯公园","UTF-8") %>林肯公园">GET方式传参</a>
	</form>
</body>
</html>

阅读更多
版权声明:本文为博主原创文章,欢迎大家转载。如有错误请多多指教。 https://blog.csdn.net/u011794238/article/details/46831009
个人分类: servlet+jsp
上一篇java乱码详解(java中byte与char的转换)
下一篇Servlet--HttpServletResponse的2个操作流的方法
想对作者说点什么? 我来说一句

JSP中文乱码解决集锦

2010年11月09日 40KB 下载

express中文乱码解决

2017年11月19日 62B 下载

smarty truncate中文乱码解决

2014年08月12日 841B 下载

java项目编译时中文乱码解决

2012年09月20日 13KB 下载

oracle中文乱码解决

2017年08月30日 193KB 下载

securecrt中文乱码解决

2015年06月19日 407B 下载

解决乱码问题

2011年11月19日 6KB 下载

没有更多推荐了,返回首页

关闭
关闭