Struts2中文乱码问题 过滤器源码分析

整理自网上:

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


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方法如下:
 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方法里找到了 prepareDispatcherAndWrapRequest方法,看其名字是对request进行预处理和封装的方法。
 1      protected HttpServletRequest prepareDispatcherAndWrapRequest(HttpServletRequest request, HttpServletResponse response)  throws Ser                 vletException {  
 2       
 3             Dispatcher du = Dispatcher.getInstance();  
 4       
 5              if (du ==  null) {  
 6       
 7                 Dispatcher.setInstance(dispatcher);  
 8                 dispatcher.prepare(request, response); // 设置编码的关键地方  
 9             }  else {  
10                 dispatcher = du;  
11             }  
12              // 省略一些代码  
13       
14              return request;  
15         } 

 

展开dispatcher.prepare(request, response)发现:
 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的属性值。
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的编码过滤器。
1  < constant  name ="struts.i18n.encoding"  value ="gbk" ></ constant >

 

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

 

转载于:https://www.cnblogs.com/shanhm1991/p/5058541.html

参与评论 您还未登录,请先 登录 后发表或查看评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值