zoukankan      html  css  js  c++  java
  • 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的过滤器放到最后,这样可以防止过滤器链被中断,导致你配置的其他过滤器不起作用
  • 相关阅读:
    ViZDoom深度预测(Depth Prediction)
    刨根问底U3D---从Profile中窥探Unity的内存管理
    关于Android真机调测Profiler
    初探Stage3D(三) 深入研究透视投影矩阵
    初探Stage3D(二) 了解AGAL
    初探Stage3D(一) 3D渲染基础原理
    unity3d优化总结篇
    Unity数据存储路径总结
    CREATE A ENERGY / HEALTH BAR HUD
    CREATE A LOADING SCENE / SPLASH SCREEN
  • 原文地址:https://www.cnblogs.com/shanhm1991/p/5058541.html
Copyright © 2011-2022 走看看