【问题标题】:How to get response body in Zuul post filter?如何在 Zuul 后置过滤器中获取响应正文?
【发布时间】:2016-05-07 10:09:08
【问题描述】:

post 过滤器中使用 Zuul 作为代理时如何读取响应正文?

我正在尝试这样调用代码:

@Component
public class PostFilter extends ZuulFilter {

    private static final Logger log = LoggerFactory.getLogger(PostFilter.class);

    @Override
    public String filterType() {
        return "post";
    }

    @Override
    public int filterOrder() {
        return 2000;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        ctx.getResponseBody(); // null

        // cant't do this, cause input stream is used later in other filters and I got InputStream Closed exception
        // GZIPInputStream gzipInputStream = new GZIPInputStream(stream);
        return null;
    }

}

【问题讨论】:

    标签: java proxy netflix-zuul


    【解决方案1】:

    我已经设法克服了这个问题。解决方案包括 4 个步骤:

    1. ctx.getResponseDataStream()读入ByteArrayOutputStream
    2. 将 OutputStream 复制到 2 个 InputStream。
    3. 将其中一个用于您的自定义目的。
    4. 使用第二个重新分配给上下文:context.setResponseBody(inputStream)
      • 从第 1 点读取流会导致无法再次读取流,因此这样您将传递一个尚未读取的新流

    【讨论】:

    • 不记得确切,但可能是的。你复制了输入流吗?
    • 复制InputStream 效果很好,但我必须在org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter 之前设置过滤顺序。使用大于 1000 的值会导致“InputStream 已关闭”错误,因为响应正文已被读取并返回。
    • 好的,很好。我在示例中使用的数字可能对我的问题不太重要;)
    • @MichaelTcourt 过滤器编号应该是响应的一部分。我尝试了几个小时,只是将过滤器编号更改为 10 并且它起作用了
    【解决方案2】:

    如果有人正在为压缩答案而苦苦挣扎,这是我使用的解决方案:

    // Read the compressed response
    RequestContext ctx = RequestContext.getCurrentContext();
    InputStream compressedResponseDataStream = ctx.getResponseDataStream();
    try {
        // Uncompress and transform the response
        InputStream responseDataStream = new GZIPInputStream(compressedResponseDataStream);
        String responseAsString = StreamUtils.copyToString(responseDataStream, Charset.forName("UTF-8"));
        // Do want you want with your String response
        ...
        // Replace the response with the modified object
        ctx.setResponseBody(responseAsString);
    } catch (IOException e) {
        logger.warn("Error reading body", e);
    }
    

    【讨论】:

    • 我错过了这条评论,花了 6 个小时在上面
    • @Cshah 在搜索 SO 中是否存在此问题之前,切勿尝试解决问题! ;) 说真的,很高兴它有帮助!
    • 如果你使用 gzip 作为 html 压缩过程
    【解决方案3】:

    感谢您的建议,这是我使用的有效代码。

    try (final InputStream responseDataStream = ctx.getResponseDataStream()) {
       final String responseData = CharStreams.toString(new InputStreamReader(responseDataStream, "UTF-8"));
       ctx.setResponseBody(responseData);
    } catch (IOException e) {
       logger.warn("Error reading body",e);
    }
    

    【讨论】:

    • 在这里很好地使用了 Java 7 的 try-with-resources 语法。如果有人担心 CharStreams.toString()@Beta (IntelliJ 对此提出警告),如果它是依赖项,也可以使用 Spring 的 StreamUtils.copyToString()。这样,也可以省略InputStreamReader 实例。另外,恕我直言,使用 StandardCharsets.UTF_8 而不是 "UTF-8" 会更干净一些。
    【解决方案4】:

    正如您在此example 中看到的,您有两种方法可用于提取响应正文:

    1- ctx.getResponseBody();

    2- ctx.getResponseDataStream();

    你必须检查哪一个不为空并使用那个。

    【讨论】:

    • 谢谢,我已经做了 id,但有点棘手。我会发布一些代码
    • 就我而言,ctx.getResponseBody();返回 null 所以我使用了 ctx.getResponseDataStream();而是
    • 刚刚发现deletectx.getResponseBody()postputgetResponseDataStream()
    【解决方案5】:

    注意过滤器编号

    使用大于 1000 的值会导致“InputStream already 关闭”错误,因为响应正文已被读取并且

    我用了 10 号,效果很好

    【讨论】:

    • 伙计,你让我开心!感谢您的回答!
    【解决方案6】:

    没有一个答案对我有用。 1)过滤器的顺序需要低于1000(发送响应过滤器)

    2) 代码:

     private String getResponseData(RequestContext ctx) throws IOException {
        String responseData = null;
    
        final InputStream responseDataStream = ctx.getResponseDataStream();
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ByteArrayOutputStream copy = new ByteArrayOutputStream();
        int read = 0;
        byte[] buff = new byte[1024];
        while ((read = responseDataStream.read(buff)) != -1) {
            bos.write(buff, 0, read);
            copy.write(buff, 0, read);
        }
        InputStream isFromFirstData = new ByteArrayInputStream(bos.toByteArray());
    
        boolean responseGZipped = ctx.getResponseGZipped();
        try {
            InputStream zin = null;
            if (responseGZipped) {
                zin = new GZIPInputStream(isFromFirstData);
            } else {
                zin = responseDataStream;
            }
            responseData = CharStreams.toString(new InputStreamReader(zin, "UTF-8"));
            ctx.setResponseDataStream(new ByteArrayInputStream(copy.toByteArray()));
    
        } catch (IOException e) {
            logger.warn("Error reading body {}", e.getMessage());
        }
    
        return responseData;
    }
    

    【讨论】:

      【解决方案7】:

      最简单的解决方案是获取一个输入流,使用InputStreamReader 读取它,最后根据读取的字符串创建一个新流,并将其设置为上下文的响应数据流。另外,不要忘记将过滤器的顺序设置为低于 1000 的数字。

      final InputStream responseDataStream = ctx.getResponseDataStream();
      String response = CharStreams.toString(new InputStreamReader(responseDataStream, StandardCharsets.UTF_8));
      InputStream stream = new ByteArrayInputStream(response.getBytes(StandardCharsets.UTF_8));
      ctx.setResponseDataStream(stream);
      

      【讨论】:

        猜你喜欢
        • 2019-11-06
        • 2019-07-08
        • 2022-07-22
        • 2020-03-23
        • 2021-01-04
        • 1970-01-01
        • 2019-04-29
        • 2014-08-18
        • 2012-08-31
        相关资源
        最近更新 更多