简体   繁体   中英

How to get response body in Zuul post filter?

How it is possible to read a response body while using Zuul as a proxy in post filter?

I am trying to call the code like this:

@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;
    }

}

I've managed to overcome this. The solution consists of 4 steps:

  1. Read ctx.getResponseDataStream() into a ByteArrayOutputStream
  2. Copy OutputStream to 2 InputStreams.
  3. Use one of it for your custom purposes.
  4. Use the second to reassign to context: context.setResponseBody(inputStream)
    • reading stream from point 1 would cause that the stream cannot be read again, so this way you're passing a new fresh stream that wasn't read yet

If someone is struggling with compressed answer, here's the solution I used:

// 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);
}

Thanks for suggestion, this is the code I used that works.

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);
}

As you can see in this example , you have two methods available to extract the response body:

1- ctx.getResponseBody();

2- ctx.getResponseDataStream();

You have to check which one is not null and use that one.

Be careful with the filterNumber

Using anything greater than 1000 leads to an "InputStream already closed" error because the response body has already been read and

I used the number 10 and worked fine

None of the answers worked for me. 1) Order of the filter needs to be lower that 1000 (sending response filter)

2) Code:

 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;
}

The simplest solution would be to get an input stream, read it with InputStreamReader , and finally create a new stream based on the read string and set it as a response data stream of context. Also, don't forget to set the order of the filter to number lower then 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);
//NOTE:filterOrder:999
RequestContext context = getCurrentContext();
InputStream stream = context.getResponseDataStream();
// uncompress
String body = StreamUtils.copyToString(new GZIPInputStream(stream),
    Charset.forName("UTF-8"));

// modify
body = "Modified gzipped response via setResponseBody(): " + body;

// compress again
ByteArrayOutputStream bos = new ByteArrayOutputStream(body.length());
GZIPOutputStream gzip = new GZIPOutputStream(bos);
gzip.write(body.getBytes("UTF-8"));
gzip.close();
byte[] compressed = bos.toByteArray();
bos.close();
context.setResponseDataStream(new ByteArrayInputStream(compressed));

https://github.com/spring-attic/sample-zuul-filters/issues/4

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM