Sun HTTPServer - 如何编写后处理过滤器?

时间:2013-01-29 21:07:14

标签: java com.sun.net.httpserver

我尝试使用Sun JRE的HTTP服务器。在阅读了两次文档后,我仍然感到困惑。

com.sun.net.httpserver.Filter javadoc说明如下

  

要求此过滤器预先/后处理给定的交换。过滤器可以:

     
      
  • 检查或修改请求标头
  •   
  • 通过创建合适的过滤器流并调用HttpExchange.setStreams(InputStream,OutputStream)来过滤请求主体或响应主体
  •   
  • 在交换中设置属性对象,其他过滤器或交换处理程序可以访问。
  •   
  • 决定:

         
        
    • 通过调用Filter.Chain.doFilter(HttpExchange)来调用链中的下一个过滤器
    •   
    • 通过不调用Filter.Chain.doFilter(HttpExchange)来终止调用链
    •   
         

    如果选项1是上面的,那么当doFilter()返回链中的所有后续过滤器时,都会被调用,并且可以检查或修改响应头。   如果上面的选项2.,那么这个过滤器必须使用HttpExchange发回适当的响应

  •   

什么不清楚,什么决定过滤器何时是过程前或过程后过滤器。 正如我所假设的,后处理过滤器在 HttpHandler之后运行,因此它可以与HttpExchange一起使用HttpHandler修改的内容。但是,过滤器只被调用一次,因此必须有一个“魔术”决定过滤器在处理程序之前或之后运行。

请帮我说清楚。

3 个答案:

答案 0 :(得分:2)

每个过滤器都是前置和后置过滤器。我的意思是请求在它通过堆栈时通过它,然后响应通过它返回到客户端的路上。调用过滤器的顺序取决于您在web.xml文件中安装过滤器的顺序。

如果您将其用作预过滤器,则可以修改InputStream,如果您想将其作为帖子,则可以修改OutputStream。您甚至可以通过链接传递自己的InputStreamOutputStream

所以,例如,您有一些Filter1Filter2Filter3。在处理之前,InputStream首先会经历Filter1,然后是Filter2,最后是Filter3。生成的OutputStream会在发送到客户端之前返回Filter3,然后Filter2,最后返回Filter1。因此,通过这种方式,您可以修改预处理和/或后处理。

答案 1 :(得分:1)

我自己对此很纳闷,我看不出setStreams方法如何对此有所帮助。我可以做到这一点的唯一方法是包装HttpExchange。

此示例显示了如何通过过滤器应用gzip压缩:

https://gist.github.com/Crydust/7e4e9228cd95febccdc58f0501c1e327

答案 2 :(得分:1)

Filter.doFilter() 只被调用一次,在 HttpHandler.handle() 被调用之前。

这意味着您无法在处理程序运行后修改 HTTP 响应的内容。所以,它更自然地是一个预过滤器。

然而,在你的 doFilter() 实现中,你可以用你自己的 FilterOutputStream 包裹响应的 OutputStream,这样你就可以拦截对 HttpExchange.getResponseBody().write(...) 的调用。

class MyFilter extends Filter {

    @Override
    public void doFilter(HttpExchange exch, Chain chain) throws IOException {

        exch.setStreams(null, new MyInterceptedOutputStream(exch.getResponseBody()));
        chain.doFilter(exch);
    }

    ...
}

这个 MyInterceptedOutputStream 类需要扩展 OutputStream 并实现其常用方法(write()close() em>)

一个技巧:您的包装器必须确保在调用 HttpExchange.sendResponseHeaders() 之前不会向原始 OutputStream 写入任何内容。这意味着您必须确保您的包装器构造函数(上面示例中的 MyInterceptedOutputStream(OutputStream os))不写入任何内容,例如与 GZIPOutputStream() 不同!

奖励:压缩流的 OutputStream 包装器示例...并在 HttpFilter.doFilter()

中工作
import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.GZIPOutputStream;

public class GZIPDelayedOutputStream extends OutputStream {

    GZIPOutputStream gzipStream;
    OutputStream originalStream;

    public GZIPDelayedOutputStream(OutputStream os) {
        super();
        originalStream = os;
    }

    private void createGzipStreamIfNecessary() throws IOException {
        if (gzipStream == null) {
            gzipStream = new GZIPOutputStream(originalStream);
        }
    }

    @Override
    public void write(int b) throws IOException {
        createGzipStreamIfNecessary();
        gzipStream.write(b);
    }

    @Override
    public void write(byte buf[]) throws IOException {
        createGzipStreamIfNecessary();
        gzipStream.write(buf);
    }

    @Override
    public void write(byte[] buf, int off, int len) throws IOException {
        createGzipStreamIfNecessary();
        gzipStream.write(buf, off, len);
    }

    @Override
    public void close() throws IOException {
        createGzipStreamIfNecessary();
        gzipStream.close();
    }
}