Как получить тело ответа в почтовом фильтре Zuul?

Как можно прочитать тело ответа при использовании Zuul в качестве прокси в post фильтре?

Я пытаюсь вызвать код следующим образом:

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

}

person Dariusz Mydlarz    schedule 29.01.2016    source источник


Ответы (6)


Мне удалось это преодолеть. Решение состоит из 4 шагов:

  1. Считайте ctx.getResponseDataStream() в ByteArrayOutputStream
  2. Скопируйте OutputStream в 2 InputStreams.
  3. Используйте один из них для ваших пользовательских целей.
  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
person Dariusz Mydlarz    schedule 12.02.2016
comment
Точно не помню, но, наверное, да. Вы скопировали входной поток? - person Dariusz Mydlarz; 17.01.2017
comment
Копирование InputStream сработало отлично, но мне пришлось установить порядок фильтров перед org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter. Использование значения больше 1000 приводит к ошибке InputStream уже закрыт, поскольку тело ответа уже было прочитано и возвращено. - person Michael Técourt; 17.01.2017
comment
Хорошо. Номер, который я использовал в примере, вероятно, не так важен для моего вопроса;) - person Dariusz Mydlarz; 17.01.2017
comment
@MichaelTecourt номер фильтра должен быть частью ответа. Я пытался часами, просто изменил номер фильтра на 10, и это сработало. - person Pedro Romão; 12.05.2017

Если кто-то борется со сжатым ответом, вот решение, которое я использовал:

// 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);
}
person VincentS    schedule 09.05.2018
comment
Чувак, я пропустил этот комментарий и потратил на него 6 часов. - person Cshah; 13.11.2018
comment
@Cshah Никогда не пытайтесь решить проблему, прежде чем искать, существует ли эта проблема в SO! ;) Серьезно, рад, что это помогло! - person VincentS; 14.11.2018
comment
если вы используете gzip в качестве процесса сжатия html - person withoutOne; 13.01.2021

Спасибо за предложение, это код, который я использовал, который работает.

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);
}
person codesalsa    schedule 29.12.2016
comment
Хорошее использование синтаксиса попытки с ресурсами Java 7 здесь. В случае, если кто-то беспокоится о том, что CharStreams.toString() является @Beta (IntelliJ предупреждает об этом), можно также использовать StreamUtils.copyToString() Spring, если это зависимость. Таким образом, можно также опустить экземпляр InputStreamReader. Кроме того, имхо, лучше использовать StandardCharsets.UTF_8 вместо "UTF-8". - person Daniel K; 12.11.2018

Как видно из этого example, у вас есть два метода извлечения тела ответа:

1- ctx.getResponseBody();

2- ctx.getResponseDataStream();

Вы должны проверить, какой из них не является нулевым, и использовать его.

person Pablo Valiente    schedule 12.02.2016
comment
Спасибо, я уже сделал id, но это было немного сложно. выложу код - person Dariusz Mydlarz; 12.02.2016
comment
в моем случае ctx.getResponseBody(); вернуть ноль, поэтому я использовал ctx.getResponseDataStream(); вместо - person Sal Prima; 30.09.2017
comment
только что обнаружил, что у delete есть ctx.getResponseBody(), а у post и put есть getResponseDataStream() - person Tiina; 27.03.2018

Будьте осторожны с номером фильтра

Использование значения больше 1000 приводит к ошибке «InputStream уже закрыт», поскольку тело ответа уже было прочитано и

Я использовал номер 10 и работал нормально

person Pedro Romão    schedule 12.05.2017
comment
Мужик, ты делаешь мой день! Спасибо за ответ! - person Carlos Cavero; 03.12.2019

Ни один из ответов не сработал для меня. 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;
}
person Damian    schedule 22.03.2019