深入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配置

<!-- spring字符集过滤器--> 

  <filter> 
  <filter-name>CharacterEncoding</filter-name> 
  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> 
  <init-param> 
  <param-name>encoding</param-name> 
  <param-value>GBK</param-value> 
  </init-param> 
  <init-param> 
  <param-name>forceEncoding</param-name> 
  <param-value>true</param-value> 
  </init-param> 
  </filter> 
  <filter> 
        <filter-name>Struts2</filter-name> 
        <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class> 
  </filter> 
  <filter-mapping> 
          <filter-name>CharacterEncoding</filter-name> 
          <url-pattern>*.action</url-pattern> 
  </filter-mapping> 
  <filter-mapping> 
        <filter-name>Struts2</filter-name> 
        <url-pattern>*.action</url-pattern> 
  </filter-mapping> 

分析过滤器源代码,找出为什么
根据filter的执行顺序知,会先执行CharacterEncoding过滤器,再执行Struts2过滤器。
CharacterEncodingFilter的核心doFilterInternal方法如下:
Java代码 复制代码  收藏代码
  1. protected void doFilterInternal(    
  2.             HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)    
  3.             throws ServletException, IOException    
  4.     
  5. if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null))    
  6.             request.setCharacterEncoding(this.encoding);//设置字符集编码    
  7.             if (this.forceEncoding && responseSetCharacterEncodingAvailable)    
  8.                 response.setCharacterEncoding(this.encoding);    
  9.                
  10.            
  11.         filterChain.doFilter(request, response);//激活下一个过滤器    
  12.        

很简洁,只要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方法里找到了prepareDispatcherAndWrap Request方法,看其名字是对request进行预处理和封装的方法。
Java代码 复制代码  收藏代码
  1.    
  2. protected HttpServletRequest prepareDispatcherAndWrapRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException    
  3.     
  4.         Dispatcher du Dispatcher.getInstance();    
  5.     
  6.         if (du == null   
  7.     
  8.             Dispatcher.setInstance(dispatcher);    
  9.             dispatcher.prepare(request, response);//设置编码的关键地方    
  10.         else    
  11.             dispatcher du;    
  12.            
  13.         //省略一些代码    
  14.     
  15.         return request;    
  16.        

展开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.        //省略了一些代码    
  8.     
  9.         if (encoding != null   
  10.             try    
  11.                 request.setCharacterEncoding(encoding);//设置了字符集编码    
  12.             catch (Exception e)    
  13.                 LOG.error("Error setting character encoding to '" encoding "' ignoring."e);    
  14.                
  15.            
  16.     //省略了一些代码    
  17.        

可以发现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代码
<constant name="struts.i18n.encoding"value="gbk"></constant> 

延伸--过滤器的其他一些思考
到了这里按说我们已经解决了问题,应该没有什么疑问了,但是前面说了,过滤器是按顺序执行的,那么我们把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.     
  3.     
  4.         HttpServletRequest request (HttpServletRequest) req;    
  5.         HttpServletResponse response (HttpServletResponse) res;    
  6.         ServletContext servletContext getServletContext();    
  7.     
  8.         String timerKey "FilterDispatcher_doFilter: "   
  9.         try    
  10.             UtilTimerStack.push(timerKey);    
  11.             request prepareDispatcherAndWrapRequest(request, response);    
  12.             ActionMapping mapping;    
  13.             try    
  14.                 mapping actionMapper.getMapping(request, dispatcher.getConfigurationManager());//关键,获取Action的映射配置    
  15.             catch (Exception ex)    
  16.                 LOG.error("error getting ActionMapping"ex);    
  17.                 dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);    
  18.                 return   
  19.                
  20.     
  21.             if (mapping == null   
  22.                //走到这里说明请求的不是一个action    
  23.                 String resourcePath RequestUtils.getServletPath(request);    
  24.     
  25.                 if ("".equals(resourcePath) && null != request.getPathInfo())    
  26.                     resourcePath request.getPathInfo();    
  27.                    
  28.     
  29.                 if (serveStatic && resourcePath.startsWith("/struts"))    
  30.                     findStaticResource(resourcePath, findAndCheckResources(resourcePath), request, response);    
  31.                 else {//很普通的一个request(非Action,非struts的静态资源)    
  32.                     chain.doFilter(request, response);//激活下一个过滤器    
  33.                    
  34.                 // The framework did its job here    
  35.                 return   
  36.                
  37.             //调用action    
  38.             dispatcher.serviceAction(request, response, servletContext, mapping);    
  39.     
  40.         finally    
  41.             try    
  42.                 ActionContextCleanUp.cleanUp(req);    
  43.             finally    
  44.                 UtilTimerStack.pop(timerKey);    
  45.                
  46.            
  47.        


看上面的代码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的过滤器放到最后,这样可以防止过滤器链被中断,导致你配置的其他过滤器不起作用。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值