`

深入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方法如下:
protected void doFilterInternal(
			HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) {
			request.setCharacterEncoding(this.encoding);//设置字符集编码
			if (this.forceEncoding && responseSetCharacterEncodingAvailable) {
				response.setCharacterEncoding(this.encoding);
			}
		}
		filterChain.doFilter(request, response);//激活下一个过滤器
	}

很简洁,只要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进行预处理和封装的方法。
protected HttpServletRequest prepareDispatcherAndWrapRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException {

        Dispatcher du = Dispatcher.getInstance();

        if (du == null) {

            Dispatcher.setInstance(dispatcher);
            dispatcher.prepare(request, response);//设置编码的关键地方
        } else {
            dispatcher = du;
        }
        //省略一些代码

        return request;
    }

展开dispatcher.prepare(request, response)发现:
public void prepare(HttpServletRequest request, HttpServletResponse response) {
        String encoding = null;
        if (defaultEncoding != null) {
            encoding = defaultEncoding;
        }

       //省略了一些代码

        if (encoding != null) {
            try {
                request.setCharacterEncoding(encoding);//设置了字符集编码
            } catch (Exception e) {
                LOG.error("Error setting character encoding to '" + encoding + "' - ignoring.", e);
            }
        }
    //省略了一些代码
    }

可以发现FilterDispatcher过滤器设置了request的字符编码,值来自defaultEncoding(看上面的代码),而defaultEncoding则是通过struts的配置文件取得的,即struts.i18n.encoding的属性值。
 @Inject(StrutsConstants.STRUTS_I18N_ENCODING)
    public static void setDefaultEncoding(String val) {
        defaultEncoding = val;
    }

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

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


        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        ServletContext servletContext = getServletContext();

        String timerKey = "FilterDispatcher_doFilter: ";
        try {
            UtilTimerStack.push(timerKey);
            request = prepareDispatcherAndWrapRequest(request, response);
            ActionMapping mapping;
            try {
                mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());//关键,获取Action的映射配置
            } catch (Exception ex) {
                LOG.error("error getting ActionMapping", ex);
                dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
                return;
            }

            if (mapping == null) {
               //走到这里说明请求的不是一个action
                String resourcePath = RequestUtils.getServletPath(request);

                if ("".equals(resourcePath) && null != request.getPathInfo()) {
                    resourcePath = request.getPathInfo();
                }

                if (serveStatic && resourcePath.startsWith("/struts")) {
                    findStaticResource(resourcePath, findAndCheckResources(resourcePath), request, response);
                } else {//很普通的一个request(非Action,非struts的静态资源)
                    chain.doFilter(request, response);//激活下一个过滤器
                }
                // The framework did its job here
                return;
            }
            //调用action
            dispatcher.serviceAction(request, response, servletContext, mapping);

        } finally {
            try {
                ActionContextCleanUp.cleanUp(req);
            } finally {
                UtilTimerStack.pop(timerKey);
            }
        }
    }

看上面的代码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的过滤器放到最后,这样可以防止过滤器链被中断,导致你配置的其他过滤器不起作用
12
1
分享到:
评论
6 楼 hua758209 2012-04-16  
这个好像对超链接里的不管用
5 楼 lmjavajas 2012-04-10  
4 楼 aaronuu 2011-12-15  
贴个源码呗 哈哈哈哈
3 楼 sky990113525 2011-10-20  
好精彩的文章,分析的很详细
2 楼 attcnbc1949 2011-02-24  
刚刚还在纠结过滤器的编码问题,看了一遍豁然开朗,好文章,鄙视下自己,又来Google了
1 楼 chenchangqun11 2011-01-02  
非常好的文章。

相关推荐

    【Struts】设置字符编码过滤器,解决乱码问题收藏

    本文将详细讲解如何通过设置字符编码过滤器(`SetCharacterEncodingFilter`)来解决这一问题,同时深入理解字符编码的原理以及在实际应用中的配置方法。 字符编码是计算机处理文本的一种方式,它将字符与数字对应...

    Struts2Action处理中文乱码

    Struts2 Action 处理中文乱码问题是一个常见的挑战,尤其是在进行Web开发时,由于编码格式不统一,可能导致输入或显示的中文字符出现乱码。以下是对两种解决方法的详细解释: 方法一:通过配置Struts2的i18n编码 ...

    struts2中文乱码问题

    综上所述,解决Struts2中文乱码问题的方法主要包括设置JSP页面编码、配置Struts2国际化编码、修改web.xml配置、使用POST请求方式以及自定义过滤器等。开发者可以根据项目的具体情况选择合适的方法来解决中文乱码问题...

    struts-hibernate-sprint开发详解 乱码问题 连接池

    接着,配置Struts2的FilterDispatcher,确保所有请求都被Struts2拦截和处理。 最后,`applicationContext.xml`是Spring的核心配置文件,包含bean的定义、事务管理以及AOP配置。例如,可以在这里配置SessionFactory...

    struts2乱码处理

    - 在`web.xml`中配置Struts2的过滤器: ```xml &lt;filter-name&gt;struts2&lt;/filter-name&gt; &lt;filter-class&gt;org.apache.struts2.dispatcher.FilterDispatcher&lt;/filter-class&gt; &lt;filter-mapping&gt; &lt;filter-name&gt;...

    struts2中的编码问题

    在探讨“Struts2中的编码问题”这一主题时,我们主要关注的是如何在Struts2框架下正确处理中文字符或任何非英文字符的编码问题,确保应用能够正确地读取、显示和处理这些字符,避免乱码现象。下面将详细阐述解决...

    解决url中文乱码.doc

    总结,解决Java Web中的URL中文乱码问题,关键在于发送请求时使用`URLEncoder.encode()`进行编码,以及在接收请求时设置正确的字符编码,如在JSP页面和Struts2 Action中。同时,可以通过配置过滤器确保整个应用程序...

    struts试题

    FilterDispatcher 是 Struts2 控制器的核心,它通常是过滤器链中的最后一个过滤器。它询问 ActionMapper 是否需要调用某个 action 来处理。如果 ActionMapper 确认需要调用某个 action,则 FilterDispatcher 把请求...

    使用拦截器解决中文乱码

    Struts1通常通过在`web.xml`中配置Filter来解决中文乱码问题,但在Struts2中,由于其内置的过滤器(如`FilterDispatcher`)会先于自定义Filter执行,因此自定义Filter往往无法生效。 #### 解决方案:使用拦截器 ...

    struts实现上传无乱码

    Struts框架是Java Web开发中常用的MVC...通过设置正确的字符编码和使用过滤器,我们可以确保整个上传流程的字符一致性,避免了乱码问题的出现。在实际项目中,可以根据具体需求调整配置和代码,以适应不同的场景。

    spring2.5.4+hibernate3.2.6+struts2+jbpm3.2.2配置方法

    `FilterDispatcher`是Struts2的核心过滤器,拦截所有请求并进行处理,而`ActionContextCleanUp`则用于清理Action上下文,确保每次请求都有独立的环境。 4. **jbpm3.2.2**: - jbpm是一个流程管理框架,用于定义、...

    struts2 中文乱码的解决办法分享

    以下是对Struts2中文乱码解决办法的详细说明: 1. **创建Filter** 为了解决这个问题,首先需要创建一个自定义的Filter,它的作用是设置请求的字符编码。创建一个名为`SetCharacterEncodingFilter`的类,这个类实现...

    建立你的第一个Struts2应用程序

    值得注意的是,为了处理中文乱码问题,需先配置`CharacterEncodingFilter`,确保其位于`FilterDispatcher`之前,这样可以保证在请求处理前就对字符编码进行转换。 #### 动态编码配置与过滤器特性 在`web.xml`中的`...

    jsp页面乱码处理

    同时,调整Struts2的过滤器顺序,确保`EncodingFilter`位于`FilterDispatcher`之前,以便在请求到达Action处理器前进行编码转换。 #### 解决方案三:配置web.xml中的过滤器 在`web.xml`中添加`EncodingFilter`,...

    Hibernate+Spring+Struts2+ExtJS开发CRUD功能.doc

    - **字符编码配置:** 为了避免乱码问题,引入`CharacterEncodingFilter`过滤器,设置请求和响应的编码为UTF-8。 **2. 全局配置文件:** - `web.xml`完整配置应包括上述所有配置项,同时可能还需要其他一些标准...

    Struts 2的用法

    2. **字符过滤**:使用`struts.i18n.encoding`配置请求和响应的编码,防止乱码问题。 3. **默认Action**:通过`default-action-ref`定义默认的Action,当请求未明确指定Action时执行。 ### Struts的拦截器 1. **...

    SSH和SSI等框架常用基础配置web.xml

    **解析**:这里定义了一个字符编码过滤器,主要用于解决中文乱码问题。`CharacterEncodingFilter`设置请求和响应的编码方式为`utf-8`。`&lt;url-pattern&gt;/*&lt;/url-pattern&gt;`表示该过滤器应用于所有请求路径。 #### 四、...

    struts总结文档

    - 在Struts2的配置中,可以通过设置`struts.i18n.encoding`常量来指定字符编码,如`GBK`,以解决中文乱码问题。在Struts2.7及以上版本,这个问题已得到解决。另外,过滤器配置也可以帮助解决乱码问题,如使用`...

    S2SH框架搭建和心得

    此外,我们还需要配置字符编码过滤器`CharacterEncodingFilter`,确保请求和响应的编码一致,避免乱码问题。这里以GBK为例,设置编码为`gbk`。 创建`struts.xml`配置文件,它是Struts2的核心配置文件,定义了Action...

Global site tag (gtag.js) - Google Analytics