5

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

com.sun.net.httpserver.Filter javadoc 说以下

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

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

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

    如果采用上述选项 1.,则当 doFilter() 返回时,Chain 中的所有后续过滤器都已被调用,并且可以检查或修改响应标头。如果采用上述选项 2,则此过滤器必须使用 HttpExchange 发送回适当的响应

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

请帮我说清楚。

4

3 回答 3

3

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

如果您将其用作 Pre 过滤器,您将修改InputStream,如果您希望将其用作 Post ,您将修改OutputStream. 您甚至可以通过自己InputStreamOutputStream链向下传递。

所以,比如说你有几个Filter1,Filter2Filter3。将InputStream首先通过Filter1,然后Filter2,最后Filter3在被处理之前。结果OutputStream将返回通过Filter3,然后Filter2最后Filter1在发送给客户端之前。因此,通过这种方式,您可以修改预处理和/或后处理。

于 2013-01-29T21:17:27.547 回答
1

我自己也想知道这一点,但我看不出该setStreams方法对此有何帮助。我能做到这一点的唯一方法是包装 HttpExchange。

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

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

于 2018-12-09T19:29:35.703 回答
1

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

这意味着在处理程序运行后,您无法修改 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()

一个技巧:您的包装器必须确保在调用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();
    }
}
于 2021-04-01T14:36:52.597 回答