Chinaunix首页 | 论坛 | 博客
  • 博客访问: 382752
  • 博文数量: 80
  • 博客积分: 2682
  • 博客等级: 少校
  • 技术积分: 907
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-16 09:55
文章分类

全部博文(80)

文章存档

2012年(80)

分类: 系统运维

2012-06-29 16:12:04

struts2 项目,通常开发过程中,一些简单的表单文件或者静态描述页面,能不走action流程就可以用html来做,可以一定程度上减少 struts2 的流程开销,如果前端有apache的话,也可以进一步分摊业务服务器压力。按照这样的原则,一个系统做下来,难免有一些或者很多的静态html文件。 
   在我的一个工程里面,所有文件的编码格式均为 utf-8,包括这些静态 html 文件。 在 IE 下访问,页面显示完全没有问题。用 httplook 查看 http 头信息,也可以看到服务器回送的页面字符集编码为正常的 utf-8。 但是用 firefox 浏览的时候,所有的动态页面(*.do)正常,唯独 html 页面全部乱码,在浏览器菜单内手动选择页面编码格式为 utf-8,则可以正常显示中文了。虽然可以暂时解决,但不可能假设所有的用户每次浏览这些静态html的时候,都会手动去选择编码格式。这里必须要有一 个治本的办法。 
    在firefox内乱码的html页面上,右键“查看页面信息”,可以看到http头信息内的编码格式为 gb2312,而页面meta信息内指定的是utf-8。即firefox是按gb2312的缺省行为来解析utf-8编码的页面,当然会乱码。问题就出 在,firefox并不会像IE那样可以根据meta信息覆盖服务器回送的http头信息,它是严格按照http协议规范的行为方式:按照http头指定 的编码格式来解析页面。也就是说,如果http头回送里面指定了页面的编码格式,firefox会忽略meta信息的字符集指定。而IE则以meta优 先。
    为什么我的服务器输出html页面的时候,http头会回送gb2312的编码集?我在 web.xml 内加上 SetCharacterEncodingFilter,强制所有输出字符集为 utf-8,问题依旧。因为项目做了 SEO,用到了 UrlRewriterFilter,便怀疑是这个 filter 在做 url forward 的时候,改变了输出字符集,可是去掉这个 filter 后,问题还是存在。 于是再考察struts2的 FilterDispatcher,这是一个全局的派发过滤器,struts2的核心派发控制器。 最初的配置如下:
   
<filter>
        
<filter-name>struts2filter-name>
        
<filter-class>org.apache.struts2.dispatcher.FilterDispatcherfilter-class>
    
filter>
    
<filter-mapping>
        
<filter-name>struts2filter-name>
        
<url-pattern>/*
        REQUEST
        FORWARD
    

url-pattern 是 /* 的全范围映射,当然也包括了 html 后缀文件。这种情况下,项目中任何一个请求,会经过3次filter,首先是SetCharacterFilter、然后是 UrlRewriterFilter、最后是 FilterDispatcher,之前已经排除了 UrlRewriterFilter 的问题。于是修改 FilterDispatcher 的 url-pattern 为 *.do,即只过滤 *.do 的请求,其他请求比如 *.html,则不会经过这个 filter。 
    这样修改后,一切都正常了。
    
    当 url-pattern 为 /* 时,所有请求包括对静态资源文件的请求,都会有FilterDispatcher来接管派发。如果请求类型为静态资源,则 FilterDispatcher 不会调用 action 处理模块来接收这个请求,而是简单的回送静态资源。在这个回送过程中,FilterDispatcher 做的工作如下:

FilterDispatcher.java
protected void findStaticResource(String name, HttpServletRequest request, HttpServletResponse response) throws IOException {
        
if (!name.endsWith(".class")) {
            
for (String pathPrefix : pathPrefixes) {
                InputStream is 
= findInputStream(name, pathPrefix);
                
if (is != null{
                    Calendar cal 
= Calendar.getInstance();
                    
                    
// check for if-modified-since, prior to any other headers
                    long ifModifiedSince = 0;
                    
try {
                        ifModifiedSince 
= request.getDateHeader("If-Modified-Since");
                    }
 catch (Exception e) {
                        LOG.warn(
"Invalid If-Modified-Since header value: '" + request.getHeader("If-Modified-Since"+ "', ignoring");
                    }

                    
long lastModifiedMillis = lastModifiedCal.getTimeInMillis();
                    
long now = cal.getTimeInMillis();
                    cal.add(Calendar.DAY_OF_MONTH, 
1);
                    
long expires = cal.getTimeInMillis();
                    
                    
if (ifModifiedSince > 0 && ifModifiedSince <= lastModifiedMillis) {
                        
// not modified, content is not sent - only basic headers and status SC_NOT_MODIFIED
                        response.setDateHeader("Expires", expires);
                        response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                        is.close();
                        
return;
                    }

                    
                    
// set the content-type header
                    String contentType = getContentType(name);
                    
if (contentType != null{
                        response.setContentType(contentType);
                    }


                    
if (serveStaticBrowserCache) {
                        
// set heading information for caching static content
                        response.setDateHeader("Date", now);
                        response.setDateHeader(
"Expires", expires);
                        response.setDateHeader(
"Retry-After", expires);
                        response.setHeader(
"Cache-Control""public");
                        response.setDateHeader(
"Last-Modified", lastModifiedMillis);
                    }
 else {
                        response.setHeader(
"Cache-Control""no-cache");
                        response.setHeader(
"Pragma""no-cache");
                        response.setHeader(
"Expires""-1");
                    }


                    
try {
                        copy(is, response.getOutputStream());
                    }
 finally {
                        is.close();
                    }

                    
return;
                }

            }

        }


        response.sendError(HttpServletResponse.SC_NOT_FOUND);
    }


// ..

protected InputStream findInputStream(String name, String packagePrefix) throws IOException {
        String resourcePath;
        
if (packagePrefix.endsWith("/"&& name.startsWith("/")) {
            resourcePath 
= packagePrefix + name.substring(1);
        }
 else {
            resourcePath 
= packagePrefix + name;
        }


        resourcePath 
= URLDecoder.decode(resourcePath, encoding);

        
return ClassLoaderUtil.getResourceAsStream(resourcePath, getClass());
    }


可以看到,由 ClassLoaderUtil.getResourceAsStream 载入静态资源,然后回送。ClassLoaderUtil 还是用了 xwork 的 lib,struts2并没有重写这个类。这个回送过程中,FilterDispatcher 并没有指定回送字符集,因此输出页面会采用服务器默认字符集,当然跟具体操作系统也有关系。目前还没看到有默认以UTF-8作为服务器字符集的。
阅读(875) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~