深入Struts2的过滤器FilterDispatcher--中文乱码及字符编码过滤器

引用

前几天在论坛上看到一篇帖子,是关于Struts2.0中文乱码的,楼主采用的是spring的字符编码过滤器(CharacterEncodingFilter)统一编码为GBK,前台提交表单数据到Action,但是在Action中得到的中文全部是乱码,前台的页面编码都是GBK没有问题。这是为什么呢?下面我们就通过阅读FilterDispatcher和CharacterEncodingFilter这两个过滤器的源代码,了解其实现细节,最终得出为什么中文还是乱码!


测试环境及其前置知识

  • Struts2.0.14
  • Spring2.5.6
  • Eclipse3.4
  • Filter的相关知识,尤其要知道Filter的执行顺序是按照web.xml中配置的filter-mapping顺序执行的。

web.xml定义文件 
这里直接采用那篇帖子的web配置

Xml代码  

  1. <!-- spring字符集过滤器 -->
  2. <filter>
  3. <filter-name>CharacterEncoding</filter-name>
  4. <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  5. <init-param>
  6. <param-name>encoding</param-name>
  7. <param-value>GBK</param-value>
  8. </init-param>
  9. <init-param>
  10. <param-name>forceEncoding</param-name>
  11. <param-value>true</param-value>
  12. </init-param>
  13. </filter>
  14. <filter>
  15. <filter-name>Struts2</filter-name>
  16. <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
  17. </filter>
  18. <filter-mapping>
  19. <filter-name>CharacterEncoding</filter-name>
  20. <url-pattern>*.action</url-pattern>
  21. </filter-mapping>
  22. <filter-mapping>
  23. <filter-name>Struts2</filter-name>
  24. <url-pattern>*.action</url-pattern>
  25. </filter-mapping>

分析过滤器源代码,找出为什么 
根据filter的执行顺序知,会先执行CharacterEncoding过滤器,再执行Struts2过滤器。 
CharacterEncodingFilter的核心doFilterInternal方法如下:

Java代码  

  1. protected void doFilterInternal(
  2. HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
  3. throws ServletException, IOException {
  4. if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) {
  5. request.setCharacterEncoding(this.encoding);//设置字符集编码
  6. if (this.forceEncoding && responseSetCharacterEncodingAvailable) {
  7. response.setCharacterEncoding(this.encoding);
  8. }
  9. }
  10. filterChain.doFilter(request, response);//激活下一个过滤器
  11. }

很简洁,只要this.encoding != null就会设置request的字符集编码,this.encoding就是web.xml中CharacterEncoding过滤器配置的encoding为GBK。 
到这里我们已经执行了一个Filter(CharacterEncoding)已经把request的字符集设置为GBK,然后执行filterChain.doFilter(request, response);//激活下一个过滤器即我们定义的Struts2过滤器。 
到这里request的字符集编码还是GBK,但是我们在Action中取得的中文为乱码,使用request.getCharacterEncoding()获取的编码为UTF-8,那么我们可以肯定问题出在FilterDispatcher过滤器上。查看FilterDispatcher的源代码,在其doFilter方法里找到了prepareDispatcherAndWrapRequest方法,看其名字是对request进行预处理和封装的方法。

Java代码  

  1. protected HttpServletRequest prepareDispatcherAndWrapRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException {
  2. Dispatcher du = Dispatcher.getInstance();
  3. if (du == null) {
  4. Dispatcher.setInstance(dispatcher);
  5. dispatcher.prepare(request, response);//设置编码的关键地方
  6. } else {
  7. dispatcher = du;
  8. }
  9. //省略一些代码
  10. return request;
  11. }

展开dispatcher.prepare(request, response)发现:

Java代码  

  1. public void prepare(HttpServletRequest request, HttpServletResponse response) {
  2. String encoding = null;
  3. if (defaultEncoding != null) {
  4. encoding = defaultEncoding;
  5. }
  6. //省略了一些代码
  7. if (encoding != null) {
  8. try {
  9. request.setCharacterEncoding(encoding);//设置了字符集编码
  10. } catch (Exception e) {
  11. LOG.error("Error setting character encoding to ‘" + encoding + "‘ - ignoring.", e);
  12. }
  13. }
  14. //省略了一些代码
  15. }

可以发现FilterDispatcher过滤器设置了request的字符编码,值来自defaultEncoding(看上面的代码),而defaultEncoding则是通过struts的配置文件取得的,即struts.i18n.encoding的属性值。

Java代码  

  1. @Inject(StrutsConstants.STRUTS_I18N_ENCODING)
  2. public static void setDefaultEncoding(String val) {
  3. defaultEncoding = val;
  4. }

如果没有配置struts.i18n.encoding的值,默认是UTF-8.现在我们明白为什么中文是乱码了,也明白了为什么在Action中获取的编码是UTF-8啦。解决方法也很简单,在struts.xml文件中配置好struts.i18n.encoding的值为GBK即可,可以选择是否去掉spring的编码过滤器。

Xml代码  

  1. <constant name="struts.i18n.encoding" value="gbk"></constant>
  2. 如果是UTF-8,那么默认可以不用配置,因为Struts2的默认常量就是utf-8

延伸--过滤器的其他一些思考 
到了这里按说我们已经解决了问题,应该没有什么疑问了,但是前面说了,过滤器是按顺序执行的,那么我们把spring的字符过滤器放在struts的过滤器后面行不行呢,想想是可以的,因为先执行struts的过滤器,设置编码为UTF-8,然后执行spring的过滤器设置成GBK。但是实际上不是那么回事,在实际调试过程中spring的过滤器压根就没有执行,为什么呢?接着看FilterDispatcher的doFilter方法

Java代码  

  1. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
  2. HttpServletRequest request = (HttpServletRequest) req;
  3. HttpServletResponse response = (HttpServletResponse) res;
  4. ServletContext servletContext = getServletContext();
  5. String timerKey = "FilterDispatcher_doFilter: ";
  6. try {
  7. UtilTimerStack.push(timerKey);
  8. request = prepareDispatcherAndWrapRequest(request, response);
  9. ActionMapping mapping;
  10. try {
  11. mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());//关键,获取Action的映射配置
  12. } catch (Exception ex) {
  13. LOG.error("error getting ActionMapping", ex);
  14. dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
  15. return;
  16. }
  17. if (mapping == null) {
  18. //走到这里说明请求的不是一个action
  19. String resourcePath = RequestUtils.getServletPath(request);
  20. if ("".equals(resourcePath) && null != request.getPathInfo()) {
  21. resourcePath = request.getPathInfo();
  22. }
  23. if (serveStatic && resourcePath.startsWith("/struts")) {
  24. findStaticResource(resourcePath, findAndCheckResources(resourcePath), request, response);
  25. } else {//很普通的一个request(非Action,非struts的静态资源)
  26. chain.doFilter(request, response);//激活下一个过滤器
  27. }
  28. // The framework did its job here
  29. return;
  30. }
  31. //调用action
  32. dispatcher.serviceAction(request, response, servletContext, mapping);
  33. } finally {
  34. try {
  35. ActionContextCleanUp.cleanUp(req);
  36. } finally {
  37. UtilTimerStack.pop(timerKey);
  38. }
  39. }
  40. }

看上面的代码mapping=actionMapper.getMapping(request,dispatcher.getConfigurationManager());这个是得到当前请求Action的信息,比如Action的名字,命名空间,result值等,只要这个mapping不为null,过滤器就会直接执行action而不会激活下一个过滤器,这就会使得spring的那个过滤器起不了作用。那么什么时候才会激活下一个过滤器呢?答案是一个很普通的请求,多么普通呢?

  • 不能是一个存在action。
  • serveStatic(struts.serve.static配置项值)为true时,不能是一个相对路径以"/struts"开头的请求,如(/struts.html,/struts/index.html),
  • 因为这样过滤器会认为你在找struts内部的静态资源,谈后它会去诸如struts的模板文件夹下去找这些静态资源。

  • 必须是一个类似于/index.html,/news/index.html这样的请求或者serveStatic为false时请求一个不存在的action。

当满足以上条件时才会激活下一个过滤器。看来这限制还挺多的,所以这就提出来一个注意事项了,当你在web.xml配置多个过滤器的时候,一定要把struts的过滤器放到最后,这样可以防止过滤器链被中断,导致你配置的其他过滤器不起作用。

时间: 2025-01-05 06:30:41

深入Struts2的过滤器FilterDispatcher--中文乱码及字符编码过滤器的相关文章

centos中文乱码修改字符编码使用centos支持中文

如何你的centos显示中文乱码,只要修改字符编码使centos支持中文就可以了,没有这个文件可以创建它,下面是修改步骤 一.中文支持 安装中文语言包: 复制代码 代码如下: yum groupinstall chinese-support 修改字符编码配置,没有这个文件就创建它: 复制代码 代码如下: vim /etc/sysconfig/i18n 修改后内容如下: 复制代码 代码如下: LANG="zh_CN.GB18030″SUPPORTED="zh_CN.UTF-8:zh_CN

servlet中的字符编码过滤器的使用

一:简介 Servlet过滤器是客户端和目标资源的中间层组件,主要是用于拦截客户端的请求和响应信息.如当web容器收到一条客户端发来的请求 web容器判断该请求是否与过滤器相关联,如果相关联就交给过滤器进行处理,处理完可以交给下一个过滤器或者其他业务,当其他业务完成 需要对客户端进行相应的时候,容器又将相应交给过滤器进行处理,过滤器处理完响应就将响应发送给客户端. 注意:上面话中的几个问题 1:web容器是如何判断请求和过滤器相关联. 2:过滤器是如何将处理完的请求交给其他过滤器的 前提知识:过

如何使用过滤器处理中文乱码

如果浏览器提交数据给Servlet的时候含中文参数,那么在Servlet中就要处理中文乱码.如果有多个Servlet都要同时接收中文参数,那么在Servlet中处理中文乱码就比较麻烦. 解决办法:使用过滤器处理中文乱码. 处理post请求的乱码 index.jsp代码 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%&g

解决Linux文档显示中文乱码问题以及编码转换

解决Linux文档显示中文乱码问题以及编码转换 使vi支持GBK编码 由于Windows下默认编码是GBK,而linux下的默认编码是UTF-8,所以打开会变成乱码.因此,我们只需要改变一下配置文件,使得vi支持GBK 打开~/.vimrc文件 加上 1.let &termencoding=&encoding2.set fileencodings=utf-8,gbk 然后:wq保存并退出 再次打开vi就显示正常了. 将其它的编码的文件转换成utf-8 另外,还可以将其它的编码的文件转换成u

asp.net 解决文本中文乱码 &nbsp; 各类文本编码转UTF-8 文本

/// <summary> /// 解决文本中文乱码   各类文本编码转UTF-8 文本 /// </summary> /// <param name="fullPath">原路径</param> /// <param name="savePath">临时保存路径</param> /// <param name="strnew">新的文件路径</param&

Servlet字符编码过滤器

在Java Web程序开发中,由于Web容器内部使用编码格式并不支持中文字符集,所以,处理浏览器请求中的中文数据就会出现乱码的现象.由于Web容器使用了ISO-8859-1的编码格式,所以在Web应用的业务处理中也会使用ISO-8859-1编码格式.虽然浏览器提交的请求使用的是中文编码格式UTF-8,但经过业务处理中的ISO-8859-1编码,仍然会出现中文乱码现象.解决此问题的方法非常简单,在业务处理中重新指定中文字符集进行编码即可解决.在实际的开发过程中,如果通过每一个业务处理指定中文字符集

jsp过滤器之encoding字符编码过滤器

一.创建两个jsp页面:a.jsp和b.jsp. 1.a.jsp 1 <!-- 登陆表单 --> 2 <form action="CheckLoginServlet.do" method="post"> 3 <input type="text" name="username"> 4 <input type="password" name="password

Java Web---登录验证和字符编码过滤器

什么是过滤器? 在Java Web中,过滤器即Filter.Servlet API中提供了一个Filter接口(javax.servlet.Filter),开发web应用时,如果编写的Java类实现了这个接口,则把这个Java类称之为过滤器Filter.通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截.简单说,就是可以实现web容器对某资源的访问前截获进行相关的处理,还可以在某资源向web容器返回响应前进行截获进行处理. 创建一个Filter的步骤 1

字符编码过滤器

<!--字符编码过滤器--> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <!--指定字符编码--> <init-param> <param-name>enc