您的位置:首页 > 运维架构 > Tomcat

利用装饰者模式解决Tomcat7中文乱码问题

2020-07-30 12:25 197 查看

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

 我们只需要定义-一个类,使其继承自这个装饰者HttpServletRequestWrapper, 就完成了
HttpServletRequest装饰者类的定义,然后重写请求参数相关方法,其它方法保持不变即可。
继承HttpServletRequestWrapper
  一个类继承自HttpServletRequestWrapper 类,要求必须重写带参构造器。目的是让装饰
者可以获取到原始的HttpServletRequest对象,然后再在装饰者中将Request进行增强。
只不过,本例中没有使用到这个原始的request。

public class CustomRequest extends HttpServletRequestWrapper {
public CustomRequest(HttpServletRequest request) {
super(request);
}
}

重写getParameterMap()

public Map<String, String[]> getParameterMap() {
// 新建一个Map,将来其中的数据是解决过乱码的。将来用户获取到的Map也是新的Map。
Map<String, String[]> newMap = new HashMap<>();
// 获取原始的Map,其中的数据是包含乱码的
Map<String, String[]> originalMap = super.getParameterMap();
// 将原始Map中的数据解决乱码问题后,写入到新的Map
try {
// 遍历原始Map
for(String key : originalMap.keySet()) {
// 获取当前遍历key的所有值
String[] values = originalMap.get(key);
// 遍历values数组,对第一个值进行中文乱码问题解决
for (int i = 0; i < values.length; i++) {
// 按照字符当前的编码进行打散,即进行编码
byte[] bytes = values[i].getBytes("ISO8859-1");
// 按照目标编码进行组装,即进行解码
values[i] = new String(bytes, "UTF-8");
}
// 将解决了乱码问题的数据放入到新的Map
newMap.put(key, values);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 返回新创建的Map
return newMap;
}

重写getParameterValues()

public String[] getParameterValues(String name) {
// 获取自定义的ParameterMap
Map<String, String[]> map = this.getParameterMap();
return map.get(name);
}

重写getParameterNames()

public Enumeration<String> getParameterNames() {
// 获取自定义的ParameterMap
Map<String, String[]> map = this.getParameterMap();
// 将Set转换为Vector
Set<String> keySet = map.keySet();
Vector keyVector = (Vector) keySet;
// 将Vector转换为Enumeration
return keyVector.elements();
}

修改CharacterEncodingFilter
  当任意请求到达时,都需要先经过该过滤器,将请求对象替换为自定义的请求对象,然
后再通过chain.doFilter()将替换过的请求向后传递。

public class MyCharactorEncodingFilter implements Filter {
//注意在Tomcat9中init(FilterConfig filterConfig)和destroy()方法已经默认被实现
//但在Tomcat7中init(FilterConfig filterConfig)和destroy()方法没有实现,所以要加上这两个方法
public void init(FilterConfig filterConfig) throws ServletException {
}
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 创建自定义的请求对象,需要强转
request = new CustomRequest((HttpServletRequest)request);
// 解决响应中文乱码问题
response.setContentType("text/html;charset=UTF-8");
chain.doFilter(request, response);
}
}

记得注册Filter

<filter>
<filter-name>CharactorEncodingFilter</filter-name>
<filter-class>*******.MyCharactorEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharactorEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

这样就解决了POST和GET请求中的中文乱码问题

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: