`
hepu
  • 浏览: 55718 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

关于Filter过滤器和Wrapper响应封装类的问题说明

    博客分类:
  • J2EE
阅读更多

在进行网站开发时需要对页面输出的信息内容进行过滤,于是编写了一个Filter过滤器由于拦截JSP和 Servlet的请求,并使用响应HttpServletResponseWrapper封装类现实响应数据的捕获。在实际应用过程中发现,在获取响应数 据并进行内容过滤后,要把数据写回response对象响应客户端时:

(1)使用tomcat5容器调用response.getOutputStream()方法即可实现,但调用 requonse.getWriter()方法时,输出二进制数据时(图片等内容无法显示)则出现“getWriter() has already been called for this response”异常。
(2)使用tomcat6容器调用response.getOutputStream()方法时有中文字符会发 生“java.io.CharConversionException:Not an ISO 8859-1 character:”异常,调用requonse.getWriter()方法时可实现文本字符串数据输出,调用 response.getOutputStream()方法可现实字节流数据的输出。
    就上述出现的问题进行分析研究,阅读了tomcat6的源代码发现,在调用response.getOutputStream()方法时会判断是否已调用 了requonse.getWriter()方法;相反在调用requonse.getWriter()方法时会判断是否已调用了 response.getOutputStream()方法。
    在tomcat5时并没有出现这个问题,使用response.getOutputStream()方法可现实两种数据输出,只是在使用 requonse.getWriter()时发生异常,而在tomcat6下则必须针对不同的数据类型选择相应输出流,这时为什么呢?仔细阅读 tomcat6源代码没有发现问题的根源,给出的参考时:在一次客户端请求的响应动作中,只能调用一种响应输出方法,要么是getWriter()要么是 getOutputStream(),且如果使用getOutputStream()方法输出字符串格式的数据时,中文无法正常通过将发生 “java.io.CharConversionException:Not an ISO 8859-1 character:”异常,在tomcat5下没有对getOutputStream()方法进行严格控制,中文字符串可正常通过。可见tomcat6 的安全机制比tomcat5要严格,对于字符串格式的数据要求使用getWriter()方法输出响应,如果使用了getOutputStream()方 法输出响应,则对输出的字符串数据进验证,要求高字节必须为0,显然中文是无法通过的。
 
源代码分析:
    //tomcat6中对用getOutputStream()方法的实现
    public ServletOutputStream getOutputStream()
        throws IOException {
 
        if (usingWriter)
            throw new IllegalStateException
                (sm.getString("coyoteResponse.getOutputStream.ise"));
 
        usingOutputStream = true;
        if (outputStream == null) {
            outputStream = new CoyoteOutputStream(outputBuffer);
        }
        return outputStream;
    }
 
 
    //tomcat6中对用getWriter ()方法的实现
    public PrintWriter getWriter()
        throws IOException {
 
        if (usingOutputStream)
            throw new IllegalStateException
                (sm.getString("coyoteResponse.getWriter.ise"));
 
        if (Globals.STRICT_SERVLET_COMPLIANCE) {    
            setCharacterEncoding(getCharacterEncoding());
        }
 
        usingWriter = true;
        outputBuffer.checkConverter();
        if (writer == null) {
            writer = new CoyoteWriter(outputBuffer);
        }
        return writer;
    }
 
 
//tomcat6中的ServletOutputStream对象的实现,其中print(String s)方法的实现
public void print(String s) throws IOException {
       if (s==null) s="null";
       int len = s.length();
       for (int i = 0; i < len; i++) {
           char c = s.charAt (i);
 
           if ((c & 0xff00) != 0) {  // high order byte must be zero
              String errMsg = lStrings.getString("err.not_iso8859_1");
              Object[] errArgs = new Object[1];
              errArgs[0] = new Character(c);
              errMsg = MessageFormat.format(errMsg, errArgs);
              throw new CharConversionException(errMsg);
           }
           write (c);
       }
}
 
    经过上述分析可知tomcat6的安全性提高了两个响应输出流是互锁关系,JSP或Servlet中到底调用了那个方法响应请求?能通过方式知道呢?难道因为提高了安全性就没有办法解决这个问题了吗?
    回过头再看看我们的过滤器和封装类的调用关系,在过滤器拦截下请求后,请求将暂时停留在过滤器的处理链中,为了获取响应数据使用了 HttpServletResponseWrapper封装类,目的是将响应的输出写入封装类中,而不是直接写入到 HttpServletResponse对象里返回给客户端,我们要对信息进行过滤,恰恰在获得响应数据后对响应的数据进行内容过滤,然后再送往客户端。
    在正常情况下tomcat容器connector处理一次客户端http请求时的处理流程是:创建HttpServletRequest请求、 HttpServletResponse响应和ClientJSPorServlet客户JSP或Servlet对象实例,然后调用service方法完 成处理。加入过滤器Filter和封装类Wrapper后情况有所改变,使调用过程发生了变化。容器在初始化时先创建Filter对象示例,当用客户端请 求时将先转给Filter进行处理(根据过滤器的url-pattern设置拦截那些请求),由Filter过滤器决定滞后的处理流程(或正常处理或中 断、转向等有代码控制);Filter将HttpServletRequest请求和封装后的HttpServletResponseWrapper响应 转给客户JSP或Servlet对象进行处理,处理结束后又回到过滤器,我们在这里加入信息过滤处理过程,然后再将数据写回 HttpServletResponse对象,响应客户端。
    由此可见,HttpServletResponseWrapper是一个封装后的“假”响应对象,我们真是使用假的响应对象获取了响应数据,只是在写回数 据调用输出流时发生了问题:在JSP或Servket中调用了getWriter()或getOutputStream()方法时,如果在Wrapper 中现实了(覆盖)这两个方法则调用Wrapper中的方法,如果没有实现,则相当于调用HttpServletResponse中方法;而我们在写回数据 时必须调用getWriter()或getOutputStream()方法。
    因此,tomcat6下:
(1)在Wrapper中现实getWriter()方法截获响应数据时,JSP或Servlet调用 getWriter()方法将数据写入Wrapper对象的缓存,处理后再调用HttpServletResponse的getWriter()方法写回 数据正常通过;但调用HttpServletResponse的getOutputStream()时由于数据是字符串流,因此中文无法通过。
(2)在Wrapper中现实getOutputStream ()方法截获响应数据时,JSP或Servlet调用getOutputStream ()方法将数据写入Wrapper对象的缓存,处理后再调用HttpServletResponse的getOutputStream ()方法写回数据正常通过。
 
    如何知道JSP或Servlet对用了那个方法呢?我们在Wrapper中实现getOutputStream()和getWriter()两个方法,并 使用标识变量标识那个方法被调用,在截获响应数据后,判断Wrapper中的那个方法被调用了,以决定何种方式读出响应数据,处理后调用 HttpServletResponse的对应输出流写回数据。
 
//实现了两种方式的HttpServletResponse封装类
public class FilterResponseWrapper
    extends HttpServletResponseWrapper {
   
    /**
     * 使用 OutputStream 输出数据
     */
    private boolean usingOutputStream=false;
   
    /**
     * 使用 PrintWriter 输出数据
     */
    private boolean usingWriter=false;
   
   
    /**
     * 输出字节流的对象,用于数据输出
     */
    private ByteArrayOutputStream outstream;
   
    /**
     * 输出字符串文本的对象,用与JSP页面的输出
     */
    private CharArrayWriter output;
   
    /**
     * 返回字符串格式数据
     * @return String
     */
    public String toString() {
        return output.toString();
    }
   
    /**
     * 返回字节数组格式的数据
     * @return byte[]
     */
    public byte[] toByteArray(){
        return outstream.toByteArray();
    }
   
    /**
     *
     * @param response HttpServletResponse
     */
    public FilterResponseWrapper(HttpServletResponse response) {
        super(response);
        output = new CharArrayWriter();
        outstream = new ByteArrayOutputStream();
    }
   
    /**
     *
     * @return PrintWriter
     */
    public PrintWriter getWriter() {
        this.usingWriter=true;
        return new PrintWriter(output);
    }
   
   
    /**
     *
     * @return ServletOutputStream
     */
    public ServletOutputStream getOutputStream(){    
        this.usingOutputStream=true;
        return new ByteOutputStream(outstream);
    }
   
   
    /**
     *
     * @return boolean
     */
    public boolean isUsingOutputStream(){
        return this.usingOutputStream;
    }
   
    /**
     *
     * @return boolean
     */
    public boolean isUsingWriter(){
        return this.usingWriter;
    }
   
}
 
//
public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
 
    FilterResponseWrapper wrapper = new FilterResponseWrapper( (HttpServletResponse)
            response);
    chain.doFilter(request, wrapper);
 
    //使用 PrintWriter 对象输出数据
    //进行内容过滤
    if(wrapper.isUsingWriter()){
        String buffer = word.process(wrapper.toString(),fileName);
        PrintWriter output = response.getWriter();
        output.print(buffer);
        output.flush();
     }
    
     //使用 ServletOutputStream 对象输出数据
     if(wrapper.isUsingOutputStream()){
         ServletOutputStream output = response.getOutputStream();
         output.write(wrapper.toByteArray());
         output.flush();
     }
     return;
}
分享到:
评论

相关推荐

    cURL封装类(Wrapper class for cURL)

    **cURL封装类详解** 在Web开发中,cURL库是一个强大的工具,它允许开发者通过命令行接口或者在编程语言中进行HTTP和其他网络协议的传输。对于PHP开发者来说,使用cURL封装类能够极大地简化与远程API交互的过程,...

    在使用实现过滤器进行request包装,获取内层request的分析

    通常,`filter`可能是对过滤器功能的进一步封装或抽象,而`manager`可能是一个管理多个过滤器的类,负责它们的注册、排序和执行。 在过滤器中,有时我们需要访问原始的请求对象,以获取未被过滤的信息。例如,用户...

    Java Service Wrapper部署说明

    在Windows的任务管理器中,你可以看到`wrapper.exe`和`java.exe`两个进程,前者是Wrapper,后者是实际运行的Java应用程序。 总的来说,Java Service Wrapper提供了一种简单、可靠的方式来在各种操作系统上部署和...

    MyBatisPlus条件构造器 -Wrapper详解.ziw

    MyBatisPlus条件构造器 -Wrapper详解(为知笔记版,可用网页打开),详解wrapper条件构造器的各种使用方法及其扩展类的使用方法。

    wrapper-windows工具包和操作说明文档

    4. **调试与日志**: `Wrapper`支持日志记录,可以通过配置`wrapper.logfile`和`wrapper.console`来指定日志输出位置,这对于调试和监控服务状态非常有用。 5. **更新服务**: 如果需要更新Java程序或修改配置,只需...

    Java Service Wrapper使用说明

    - `wrapper.java.classpath`:设定类路径。 - `wrapper.java.library.path`:指明Wrapper库文件位置。 - `wrapper.java.mainclass`:指定主类。 - `wrapper.app.parameter`:传递给应用的参数。 - `wrapper.nt...

    JAVA Web 用过滤器和包装器消除乱码

    过滤器(Filter)和包装器(Wrapper)是解决此类问题的有效工具。本文将深入探讨如何使用JAVA Web的过滤器和包装器来消除乱码问题。 首先,我们来看过滤器(Filter)。在JAVA Web中,过滤器是Servlet规范的一部分,...

    ADO C++封装类

    - 头文件中的使用说明可能包含了如何实例化封装类,如何调用成员方法,以及如何处理返回的Recordset对象等信息。例如,如何打开连接: ```cpp MyADOCPPWrapper wrapper; wrapper.OpenConnection("DSN=...

    c++ ODBC 封装类

    最后,`odbcconn.doc`文件应该是关于这个封装类的文档,详细解释了如何使用这些API,可能包括以下内容: 1. **安装和配置ODBC数据源**:用户需要在操作系统中设置ODBC数据源,包括驱动选择和连接信息。 2. **实例...

    windows关于qt的数据库操作封装类以及用例,c++代码

    windows 关于qt的数据库操作封装类 以及用例: SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它的功能特点有: 1. ACID事务 2. 零配置 – 无需安装和管理配置 3....

    mybatis-plus QueryWrapper条件查询器

    MyBatis-Plus的QueryWrapper是其强大的查询构造器,为开发者提供了便利的API来构建复杂的SQL查询。在Spring Boot项目中,MyBatis-Plus作为一个扩展MyBatis的轻量级框架,大大简化了数据库操作。下面我们将深入探讨...

    wrapper 部署linux跟window

    标题中的"Wrapper部署Linux跟Windows"指的是在操作系统层面(Linux和Windows)使用Wrapper技术来封装应用程序,以便在不同环境中统一管理和运行。Wrapper通常是一个小型的控制程序,它负责调用和管理目标应用,同时...

    Java Service Wrapper使用总结

    下面是 Java Service Wrapper 使用总结的详细说明: 概述 Java Service Wrapper 是一个开源的项目,旨在将 Java 应用程序转换为一个可安装的服务。它提供了一种简单的方式来将 Java 应用程序打包成一个独立的服务...

    wrapper-linux-x86-64-3.5.55 社区版

    Wrapper技术是将Java程序封装到一个可执行的系统服务或守护进程中,使其能够更方便地管理、监控和自动化启动。 首先,"wrapper-linux-x86-64-3.5.55"这个版本表明它是专为64位Linux操作系统设计的,兼容x86_64架构...

    Web开发 乱码过滤器.zip

    总结来说,这个压缩包提供了处理Web应用中乱码问题的解决方案,包括了自定义HttpServletRequestWrapper来正确解码GET参数,以及通过字符编码过滤器统一设置请求和响应的编码。这样的做法可以有效避免由于编码不一致...

    wrapper配置

    Wrapper配置是将Java应用程序集成到操作系统服务中的技术,主要用于...以上就是关于"wrapper配置"的基本知识和步骤,通过熟练掌握这些内容,你可以有效地将Java应用程序集成到操作系统服务中,实现自启动和高效管理。

    Kompex SQLite Wrapper Source

    当 SQLite 引擎返回错误时,`KSQLite` 类会捕获这些错误,并通过 `lastError()` 方法提供错误信息,帮助开发者调试和解决问题。 在实际使用 Kompex SQLite Wrapper 时,开发者需要注意内存管理和线程安全。由于 ...

    Wrapper将Java Application程序封装为Windows下面的服务

    例如,设置`wrapper.java.mainclass`属性为你的主类,`wrapper.java.classpath`属性为你的类路径。 2. **添加启动参数**: 如果你的Java应用需要特定的启动参数,你可以在`wrapper.conf`中添加这些参数。例如,`...

    windows wrapper

    1. **Wrapper相关文档**:这些文档通常包含了关于如何配置和使用Windows Wrapper的详细指南。它们可能包括安装步骤、配置文件的格式、设置参数的方法以及解决常见问题的技巧。通过这些文档,开发者可以了解如何将...

    QueryWrapper 常用用法

    QueryWrapper 常用用法

Global site tag (gtag.js) - Google Analytics