从HttpServletResponse里拿到PrintWriter的内容

生来就可爱ヽ(ⅴ<●) 提交于 2019-12-10 14:44:53

在JavaWeb企业级应用中,接口是一个很常见的功能模块。此处不讨论如下问题(请自行搜索):

  • 接口是什么
  • 为什么要使用接口
  • .....

一般的接口代码中会有如下代码片段(此处省略接收参数、处理异常请求等功能代码):

    PrintWriter writer = null;
    try {
        writer = response.getWriter();
        String msg = "<xml><msg>hahah</msg><createAt>"
                +System.currentTimeMillis()+"</createAt></xml>";
        writer.print(msg);
        writer.flush();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (writer != null) {
            writer.close();
        }
    }

很简单,如上代码将msg的内容响应给请求者,请求者将获取到msg的内容。

几天前遇到一个问题,要在Struts2框架中使用拦截器,来截取请求报文与响应报文。(个人认为该功能应该由核心处理器去统一完成。)不论能否实现,且试一下。

上面的代码中很明显将一个字符串放在response里响应给请求者。那我们能不能在这里做文章呢。 Debug一下writer的值看能不能有什么线索。

我们可以在writer.flush()之后添加点代码:

    PrintWriter writer = null;
    try {
        writer = response.getWriter();
        String msg = "<xml><msg>hahah</msg><createAt>"
                +System.currentTimeMillis()+"</createAt></xml>";
        writer.print(msg);
        writer.flush();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (writer != null) {
            writer.close();
        }
    }
    
    try {
            PrintWriter writerToBeRead = response.getWriter();
            System.out.print(writerToBeRead.getClass().getName());                
    } catch (Exception e) {
        e.printStackTrace();
    }

我们在

System.out.print(writerToBeRead.getClass().getName());

这一行添加断点进行调试。 正好看到了要响应给请求者的信息。

PrinterWriter

可以看到区域3中存放的信息就是msg,它的内容在区域5中清晰地显示着。而区域3是区域2的成员属性(其实区域4这个对象也和区域2一样)。

最重要的是输出的内容

org.apache.catalina.connector.CoyoteWriter

正好我们在上图的区域1中看到了

"writerToBeRead"=CoyoteWriter

说明writerToBeRead这个对象是org.apache.catalina.connector.CoyoteWriter类型的。如果您了解tomcat源代码,可以知道该类在TOMCAT_BASE/lib/catalina.jar中,这个jar由tomcat主程序调用,不被程序员显式使用,所以在添加dependency时添加provided类型的scope,由于它还会引入coyote.jar文件,故也将其排除。

<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>catalina</artifactId>
    <version>6.0.45</version>
   <scope>provided</scope>  
    <exclusions>
        <exclusion>
            <artifactId>coyote</artifactId>
            <groupId>org.apache.tomcat</groupId>
        </exclusion>
    </exclusions>
</dependency>

然后我们就将writerToBeRead强制转换成CoyoteWriter

    PrintWriter writerToBeRead = response.getWriter();
    CoyoteWriter cw = (CoyoteWriter)writerToBeRead;

然后在eclipse中按ctrl点击CoyoteWriter,可以看到它有一个受保护的成员属性ob

CoyoteWriter

下面要进行的就是——反射了。 先取出ob的内容,代码如下 :

        Class<?> clazz = cw.getClass();
        Field declaredField = clazz.getDeclaredField("ob");
        declaredField.setAccessible(true);
        OutputBuffer ob = (OutputBuffer)declaredField.get(cw);

同样点击OutputBuffer,可以看到它有一个私有属性outputChunk

OutputBuffer

不管三七二十一,拿出来再说。

        Class<?> classOutputBuffer = ob.getClass();
        Field fieldOutputChunk = classOutputBuffer.getDeclaredField("outputChunk");
        fieldOutputChunk.setAccessible(true);
        Object object = fieldOutputChunk.get(ob);
        System.out.println("text from response: "+ object);

哈,我们拿到想要的数据了。

多说一句,上面的object的类型是`org.apache.tomcat.util.buf.ByteChunk`,感兴趣的可以看下tomcat源码,了解下它的toString()方法或其它的,tomcat真的是大师级作品啊。

博客也至此结束了。 欢迎参观我的博客,拍砖~。

  • 提供项目文件:http://git.oschina.net/valuetodays/blog-attachment/raw/master/oschina/%E4%BB%8EHttpServletResponse%E9%87%8C%E6%8B%BF%E5%88%B0PrintWriter%E7%9A%84%E5%86%85%E5%AE%B9/getDataFromResponse.zip

  • 提供源文件内容:

          public void doGetDataFromResponse(HttpServletResponse response) {
              PrintWriter writer = null;
              try {
                  writer = response.getWriter();
                  String msg = "<xml><msg>hahah</msg><createAt>"
                          +System.currentTimeMillis()+"</createAt></xml>";
                  writer.print(msg);
                  writer.flush();
              } catch (IOException e) {
                  e.printStackTrace();
              } finally {
                  if (writer != null) {
                      writer.close();
                  }
              }
    
              try {
                  PrintWriter writerToBeRead = response.getWriter();
                  System.out.print(writerToBeRead.getClass().getName());
                  CoyoteWriter cw = (CoyoteWriter)writerToBeRead;
                  LOG.debug(cw.getClass().getName());
                  Class<?> clazz = cw.getClass();
                  Field declaredField = clazz.getDeclaredField("ob");
                  declaredField.setAccessible(true);
                  OutputBuffer ob = (OutputBuffer)declaredField.get(cw);
                  LOG.debug(ob);
                  Class<?> classOutputBuffer = ob.getClass();
                  Field fieldOutputChunk = classOutputBuffer.getDeclaredField("outputChunk");
                  fieldOutputChunk.setAccessible(true);
                  Object object = fieldOutputChunk.get(ob);
                  LOG.info(object.getClass().getName());
                  LOG.info("text from response: "+ object);
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
    
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!