如何在 Zuul 后置过滤器中获取响应正文?
Posted
技术标签:
【中文标题】如何在 Zuul 后置过滤器中获取响应正文?【英文标题】:How to get response body in Zuul post filter? 【发布时间】: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;
【问题讨论】:
【参考方案1】:我已经设法克服了这个问题。解决方案包括 4 个步骤:
-
将
ctx.getResponseDataStream()
读入ByteArrayOutputStream
将 OutputStream 复制到 2 个 InputStream。
将其中一个用于您的自定义目的。
使用第二个重新分配给上下文: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();而是 刚刚发现delete
有ctx.getResponseBody()
但post
和put
有getResponseDataStream()
【参考方案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);
【讨论】:
以上是关于如何在 Zuul 后置过滤器中获取响应正文?的主要内容,如果未能解决你的问题,请参考以下文章