You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Cloud Gateway 4.1.0:全局后置过滤器只读读取响应体方案问询

Spring Cloud Gateway 4.1.0 全局后置过滤器只读响应体方案

针对你提出的在全局后置过滤器中只读响应体、不修改,且适配现有过滤器执行顺序的需求,以下是更简洁的实现方案及关键说明:

核心实现逻辑

由于Spring Cloud Gateway基于WebFlux,响应体是流式数据(只能被消费一次),因此必须通过ServerHttpResponseDecorator拦截响应流,在数据被写入客户端前读取副本,同时保证原响应体正常传递。以下是简化的只读实现:

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import java.nio.charset.StandardCharsets;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;

@Component
public class ReadOnlyResponseBodyFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpResponse originalResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();

        // 装饰响应,仅读取响应体副本
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    Flux<DataBuffer> fluxBody = (Flux<DataBuffer>) body;
                    // 拦截流,读取副本后原封不动传递
                    return super.writeWith(fluxBody.doOnNext(dataBuffer -> {
                        // 读取响应体字节
                        byte[] content = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(content);
                        // 释放buffer,避免内存泄漏
                        DataBufferUtils.release(dataBuffer);
                        // 转换为字符串(可根据实际响应格式调整编码)
                        String responseBody = new String(content, StandardCharsets.UTF_8);
                        
                        // 这里执行你的只读操作,比如日志、监控上报等
                        System.out.println("捕获到响应体:" + responseBody);
                    }));
                }
                return super.writeWith(body);
            }
        };

        // 替换响应对象,继续执行过滤器链
        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }

    @Override
    public int getOrder() {
        // 必须在NettyWriteResponseFilter(order=-1)之前执行,保证响应流未被消费
        return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
    }
}

关键说明

  1. 顺序问题的必然性:
    NettyWriteResponseFilter是网关中实际将响应写入客户端的过滤器(order=-1),如果自定义过滤器在它之后执行,响应流已经被消费,无法再读取。因此必须将只读过滤器的order设为小于-1(示例中用常量减1,避免硬编码)。

    你的现有过滤器(order=998、999、1000)会在NettyWriteResponseFilter之后执行,若这些过滤器不需要读取响应体,该方案不会影响它们的执行顺序和逻辑;若需要读取,需同步调整它们的order至小于-1。

  2. 只读操作的保证:
    实现中仅通过doOnNext读取响应体副本,未修改流的内容或结构,原响应体将完整传递给客户端,完全符合只读需求。

  3. 为什么没有更直接的方式:
    WebFlux的响应式模型决定了流式数据只能被消费一次,无法直接从ServerWebExchange中获取响应体,必须通过装饰器拦截流的方式实现读取,这是最简洁且符合框架设计的方案。

内容的提问来源于stack exchange,提问作者Deepak Chhetri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 11:53:08