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; } }
关键说明
顺序问题的必然性:
NettyWriteResponseFilter是网关中实际将响应写入客户端的过滤器(order=-1),如果自定义过滤器在它之后执行,响应流已经被消费,无法再读取。因此必须将只读过滤器的order设为小于-1(示例中用常量减1,避免硬编码)。你的现有过滤器(order=998、999、1000)会在
NettyWriteResponseFilter之后执行,若这些过滤器不需要读取响应体,该方案不会影响它们的执行顺序和逻辑;若需要读取,需同步调整它们的order至小于-1。只读操作的保证:
实现中仅通过doOnNext读取响应体副本,未修改流的内容或结构,原响应体将完整传递给客户端,完全符合只读需求。为什么没有更直接的方式:
WebFlux的响应式模型决定了流式数据只能被消费一次,无法直接从ServerWebExchange中获取响应体,必须通过装饰器拦截流的方式实现读取,这是最简洁且符合框架设计的方案。
内容的提问来源于stack exchange,提问作者Deepak Chhetri
相关产品推荐
相关产品推荐

