使用Filter解决乱码问题

使用过滤器Filter解决乱码问题

在之前编写servlet时,需要在每个servlet中声明编码,这种方式比较繁琐,下面使用Filter解决该问题。

定义一个Filter

package com.monkey1024.filter;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 * 字符编码Filter
 */
public class CharacterEncodingFilter implements Filter {

    public void init(FilterConfig fConfig) throws ServletException {

    }
    public void destroy() {

    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //设置post请求的编码
        request.setCharacterEncoding("utf-8");
        //设置响应编码
        response.setContentType("text/html;charset=utf-8");

        chain.doFilter(request, response);
    }



}

在web.xml文件中注册Filter

<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>com.monkey1024.filter.CharacterEncodingFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

定义一个LoginServlet来测试是否真正解决乱码问题并在web.xml文件中注册

package com.monkey1024.servlet;

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

/**
 * 登录
 */
public class LoginServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        System.out.println(username);

        response.getWriter().print("用户名为:" + username);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}

编写JSP:

<form action="/login" method="post">
    用户名:<input type="text" name="username">
    <input type="submit" value="登录">
</form>

同时解决get和post乱码问题

上面的代码中只能解决post方式的乱码问题,然而对于get方式依然不起作用。

下面我们再来分析一下乱码产生的原因:当浏览器将包含有中文(例如 UTF-8 编码)参数的请求(无论是 GET 还是 POST 请求),以字节序列的形式发送到服务器后,服务器会按照其默认的字符编码ISO8859-1 进行解码,并将解码后的字符存放到 ParameterMap 中。此时的 ParameterMap 中存放的字符其实已经是乱码了,因为将 UTF-8 的字符序列解码为ISO8859-1 的字符,当然会出现乱码。要从根本上解决这个乱码问题,我们可以将ParameterMap中的参数重新按照UTF-8进行编码,这样Servlet 再从 ParameterMap 中读取参数,就不会出现乱码了。
不过向 ParameterMap 中存放数据是由服务器自动完成的,“向 ParameterMap中存放数据”这个时间点程序员无法捕获。我们的解决方案思路是,自定义一个HttpServletRequest 类型,该类型是HttpServletRequest 的一个装饰者。让这个装饰者重写HttpServletRequest 中请求参数相关方法。例如,重写 getParameterMap()方法,在该方法中定义一个 Map,并将原始 ParameterMap 中存放的乱码问题解决后,将数据存放到这个新的Map 中。然后,再重写其它参数相关方法,让这些方法获取请求参数,直接从这个新的 Map中获取。这样乱码问题就得以解决。也就是说,将来整个应用中所有的请求对象将使用我们自定义的这个请求的装饰者。这个替换工作可以通过过滤器完成,即所有请求到达应用后,首先经过这个过滤器,将HttpServletRequest 请求替换为装饰者。

修改上面代码中的Filter中添加代码如下:

// 自定义request对象
class MyRequest extends HttpServletRequestWrapper {

    private HttpServletRequest request;

    private boolean hasEncode;

    public MyRequest(HttpServletRequest request) {
        super(request);// super必须写
        this.request = request;
    }

    // 对需要增强方法 进行覆盖
    @Override
    public Map getParameterMap() {
        // 先获得请求方式
        String method = request.getMethod();
        if (method.equalsIgnoreCase("post")) {
            // post请求
            try {
                // 处理post乱码
                request.setCharacterEncoding("utf-8");
                return request.getParameterMap();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        } else if (method.equalsIgnoreCase("get")) {
            // get请求
            Map<String, String[]> parameterMap = request.getParameterMap();
            if (!hasEncode) { // 确保get手动编码逻辑只运行一次
                for (String parameterName : parameterMap.keySet()) {
                    String[] values = parameterMap.get(parameterName);
                    if (values != null) {
                        for (int i = 0; i < values.length; i++) {
                            try {
                                // 处理get乱码
                                values[i] = new String(values[i]
                                        .getBytes("ISO-8859-1"), "utf-8");
                            } catch (UnsupportedEncodingException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
                hasEncode = true;
            }
            return parameterMap;
        }

        return super.getParameterMap();
    }

    @Override
    public String getParameter(String name) {
        Map<String, String[]> parameterMap = getParameterMap();
        String[] values = parameterMap.get(name);
        if (values == null) {
            return null;
        }
        return values[0]; // 取回参数的第一个值
    }

    @Override
    public String[] getParameterValues(String name) {
        Map<String, String[]> parameterMap = getParameterMap();
        String[] values = parameterMap.get(name);
        return values;
    }

}