Как читать тело ответа в POST-фильтре Spring Cloud Gateway (Webflux)? - PullRequest
0 голосов
/ 11 декабря 2019

Spring Cloud Gateway документирует фазу пост-фильтрации, например: https://cloud.spring.io/spring-cloud-static/Greenwich.RELEASE/single/spring-cloud.html#_writing_custom_gatewayfilter_factories

Я реализовал фильтр, который не имеет ничего на этапе предфильтрации, но я хотел бы манипулировать телом ответа на этапе пост-фильтрапрежде чем передать его клиенту, ожидающему ответа, чтобы он был успешным.

Мне удалось успешно изменить ответ, но я не нашел решения, как на самом деле прочитать исходный ответ из успешного прокси-запроса. Я думаю, что перепробовал почти все, и я знаю, что, вероятно, не самая лучшая практика - читать тело в середине потока webflux, поскольку это как бы побеждает всю цель. Но это сводит меня с ума, так сложно просто прочитать данные из ответа.

Мой фильтр выглядит так:

class PostFilter : AbstractGatewayFilterFactory<PostFilter.Config>(Config::class.java) {
    @Autowired
    private lateinit var tokenService: TokenService

    /* This is required when implementing AbstractGatewayFilterFactory.
     * With config we could pass filter configurations in application.yml route config
     * This can be used if it is needed to parametrise filter functionality
     */
    class Config

    override fun apply(config: Config?): GatewayFilter {
        return GatewayFilter { exchange: ServerWebExchange, chain: GatewayFilterChain ->

            chain.filter(exchange).then(Mono.defer {
                val response = exchange.response
                val bytes: ByteArray = "Some text".toByteArray(StandardCharsets.UTF_8)
                val buffer: DataBuffer = exchange.response.bufferFactory().wrap(bytes)
                response.headers.contentLength = bytes.size.toLong()

                // How do I get the original response?
                response.writeWith(Flux.just(buffer))
            })


        }
    }
}

Я нашел этот пример: https://github.com/spring-cloud/spring-cloud-gateway/blob/master/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WebClientWriteResponseFilter.java гдеЯ вижу ClientResponse clientResponse = exchange.getAttribute(CLIENT_RESPONSE_ATTR);, но когда я пытаюсь это сделать, он не содержит ClientResponse объекта, который можно было бы использовать для извлечения тела. Вместо этого я получаю HttpClientResponse объект netty, который никак не может получить тело ответа.

Я искал множество тем в stackoverflow, посвященных проблемам, которые выглядят немного похожими, но ни одна из них на самом деле не одинакова.

Я также попробовал этот подход, как в этом https://github.com/spring-cloud/spring-cloud-gateway/issues/177#issuecomment-361411981, где вы можете использовать ServerHttpResponseDecorator для чтения тела. Тем не менее, он читает тело, только если вызван метод writeWith(). Поэтому, если я хочу получить исходный ответ, мне нужно получить исходный ответ в виде DataBuffer, чтобы записать его снова, чтобы был запущен writeWith. Но если бы у меня было DataBuffer, мне бы не понадобилось всего ServerHttpResponseDecorator.

Так что, пожалуйста, помогите мне - я ударился головой о стену и чувствую, что отказываюсь от этого,Я обычно никогда не сдаюсь, так как считаю, что это должно быть даже тривиально, потому что это такая простая вещь: «прочитать тело ответа после прокси-запроса».

Есть идеи?

1 Ответ

0 голосов
/ 11 декабря 2019

Один из способов получить ответ в читаемом формате (адаптировано из NettyWriteResponseFilter: https://github.com/spring-cloud/spring-cloud-gateway/blob/master/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/NettyWriteResponseFilter.java) - использовать Netty Connection:

            val connection: Connection = exchange.getAttribute(CLIENT_RESPONSE_CONN_ATTR) ?: error("Connection not found")
            val factory: NettyDataBufferFactory = response.bufferFactory() as NettyDataBufferFactory
            val body = connection.inbound().receive().retain().map(factory::wrap).next()
            body.doOnSuccess {    
                val dest = ByteArray(it.readableByteCount())
                it.read(dest)
                val originalResponse = String(dest, StandardCharsets.UTF_8)
                println(originalResponse)
            }
...