问题概述:web开发中,常常会遇到中文乱码问题,很令人头疼,今天准备总结一下乱码产生的原因及解决方案。
首先,要具备一些基础知识:客户端浏览器与服务器之间是依靠HTTP协议进行通信的,客户端发送请求,服务器接收到请求处理后给出响应。客户端请求方式有get和post两种,get请求将请求参数拼接到URL后,post请求将请求参数封装到实体中。服务器端响应可通过response的getWriter方法或getOutputStream方法向浏览器输出数据。这里可能出现乱码的地方有:请求参数乱码,响应输出乱码。
中文乱码问题产生的根本原因:编解码所用的码表不一致。
1.请求参数乱码:
浏览器以什么编码来发送请求参数?浏览器以什么编码打开的表单页面,就用什么编码发送这个页面提交的数据
服务器以什么编码来打开呢?如果不指定,则使用ISO8859-1,这样如果请求参数中有中文必然就乱码了
解决方案:
对于POST提交,可以设置request.setCharacterEncoding("utf-8");明确的通知服务器以浏览器发送过来的编码来打开数据就可以解决乱码。但是这个方法只对请求中实体内容部分起作用,所以GET提交的乱码并不能解决.
对于GET提交的乱码,只能手动的进行编解码从而解决乱码问题:
String username = request.getParameter("username"); username = new String(username.getBytes("iso8859-1"),"utf-8");
2.响应输出乱码:
解决方案:
response.getOutputStream().write("中文".getBytes())输出数据,这是一个字节流,是什么字节输出什么字节,而浏览器默认用平台字节码打开服务器发送的数据,如果服务器端使用了非平台码去输出字符的字节数据就需要明确的指定浏览器编码时所用的码表,以防止乱码问题。
response.addHeader("Content-type","text/html;charset=gb2312");
response.getWriter().write(“中文”);输出数据,这是一个字符流,response会将此字符进行转码操作后输出到浏览器,这个过程默认使用ISO8859-1码表,而ISO8859-1中没有中文,于是转码过程中用?代替了中文,导致乱码问题。可以指定response在转码过程中使用的目标码表,防止乱码。response.setCharcterEncoding("gb2312");
其实response还提供了setContentType("text/html;charset=gb2312")方法,此方法会设置content-type响应头,通知浏览器打开的码表,同时设置response的转码用码表,从而一行代码解决乱码。
当然,以上解决方案只适用于个人学习或小型项目开发。在实际开发中,当业务逻辑过多时,应当用一个全站乱码过滤器来彻底解决所有乱码,而不用每次都手动去写,简化开发。下面的全站乱码过滤器导入项目中即可使用,要注意的是:1.需在web.xml中配置过滤器,为了能达到全站乱码过滤的目的,<filter-mapping>节点中的<url-pattern>应配置为 /* 。 2.其中用到的全站编码encode要在web.xml中的<context-param>节点下配置,便于日后修改。
全站乱码过滤器代码:
import java.io.IOException;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class EncodeFilter implements Filter {
private FilterConfig config = null;
private ServletContext context = null;
private String encode = null;// 全站编码
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 响应乱码解决
response.setContentType("text/html;charset="+encode);
// 利用装饰设计模式改变request对象和获取请求参数相关的方法,从而解决乱码问题
chain.doFilter(new MyHttpServletRequest((HttpServletRequest)request), response);
}
public void init(FilterConfig config) throws ServletException {
this.config = config;
this.context = config.getServletContext();
encode = context.getInitParameter("encode");// 获取全站参数
}
private class MyHttpServletRequest extends HttpServletRequestWrapper{
private HttpServletRequest request = null;
private boolean isNotEncode = true;
public MyHttpServletRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public Map<String,String[]> getParameterMap() {
try{
if(request.getMethod().equalsIgnoreCase("POST")){// 解决post提交的乱码
request.setCharacterEncoding(encode);
return request.getParameterMap();
}else if(request.getMethod().equalsIgnoreCase("GET")){<span style="font-family: Arial, Helvetica, sans-serif;">// 解决get提交的乱码</span>
Map<String,String[]> map = request.getParameterMap();
if(isNotEncode){
for(Map.Entry<String,String[]> entry : map.entrySet()){
String[] vs = entry.getValue();
for(int i = 0;i < vs.length;i++){
vs[i] = new String(vs[i].getBytes("iso-8859-1"),encode);// 手动编解码
}
}
isNotEncode = false;
}
return map;
}else{
return request.getParameterMap();
}
}catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
@Override
public String getParameter(String name) {
return getParameterValues(name) == null ? null : getParameterValues(name)[0];
}
@Override
public String[] getParameterValues(String name) {
return getParameterMap().get(name);
}
}
}