Spring Gateway 同时拦截输入输出做日志操作

发布时间 2023-11-10 17:53:20作者: cococooder

Spring Gateway 同时拦截输入输出做日志操作,包括request body, 和response body

主要靠代理模式,参考

https://stackoverflow.com/questions/47182961/copy-of-the-request-response-body-on-a-spring-reactive-app

需要创建 request , response, ServerWebExchangeDecorator, GlobalFilter

public class PartnerServerHttpRequestDecorator extends ServerHttpRequestDecorator {
    public PartnerServerHttpRequestDecorator(ServerHttpRequest delegate) {
        super(delegate);
    }

    private final StringBuilder cachedBody = new StringBuilder();


    @Override
    public Flux<DataBuffer> getBody() {
        return super.getBody().doOnNext(this::cache);
    }

    private void cache(DataBuffer buffer) {
        cachedBody.append(UTF_8.decode(buffer.asByteBuffer()));
    }

    public String getCachedBody() {
        return cachedBody.toString();
    }
}

  

public class PartnerServerHttpResponseDecorator extends ServerHttpResponseDecorator {

    private final StringBuilder cachedBody = new StringBuilder();
    public PartnerServerHttpResponseDecorator(ServerHttpResponse delegate) {
        super(delegate);
    }

    @Override
    public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
        if (body instanceof Flux) {
            Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
            return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
                // 1.获取response中的内容
                DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
                DataBuffer join = dataBufferFactory.join(dataBuffers);
                byte[] content = new byte[join.readableByteCount()];
                join.read(content);
                DataBufferUtils.release(join);
                String bodyStr = new String(content, StandardCharsets.UTF_8);
                cachedBody.append(bodyStr);
                return bufferFactory().wrap(bodyStr.getBytes());
            }));
        }
        return super.writeWith(body);
    }


    public String getCachedBody() {
        return cachedBody.toString();
    }
}

  

public class PartnerServerWebExchangeDecorator  extends ServerWebExchangeDecorator  {


    private final ServerHttpRequestDecorator requestDecorator;
    private final ServerHttpResponseDecorator responseDecorator;

    public PartnerServerWebExchangeDecorator(ServerWebExchange delegate) {
        super(delegate);
        this.requestDecorator = new PartnerServerHttpRequestDecorator(delegate.getRequest());
        this.responseDecorator = new PartnerServerHttpResponseDecorator(delegate.getResponse());
    }

    @Override
    public ServerHttpRequest getRequest() {
        return requestDecorator;
    }

    @Override
    public ServerHttpResponse getResponse() {
        return responseDecorator;
    }
}

  然后实现GlobalFilter 即可

@Slf4j
@Component
public class OperatorLogPostFilter implements GlobalFilter, Ordered {

   
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("entry OperatorLogPostFilter");
        // 使用之前声明的
        PartnerServerWebExchangeDecorator partnerServerWebExchangeDecorator = new PartnerServerWebExchangeDecorator(exchange);

        return chain.filter(partnerServerWebExchangeDecorator).then(Mono.fromRunnable(()-> {
            // 实际返回的就是自己装饰/代理 好的类型
            PartnerServerHttpRequestDecorator request = (PartnerServerHttpRequestDecorator) partnerServerWebExchangeDecorator.getRequest();
            PartnerServerHttpResponseDecorator response = (PartnerServerHttpResponseDecorator) partnerServerWebExchangeDecorator.getResponse();
            // 记录的操作在这里进行
            log.info("request api:{},request header:{}, request body:{}, response body:{}",
                    request.getPath().value(),
                    request.getHeaders().getFirst(CommonHeader.ACCOUNT_NAME),
                    request.getCachedBody(),
                    response.getCachedBody());
        }));

    }



    @Override
    public int getOrder() {
    // 这个地方非常坑,一定要小于-1才会进入到response 的代理中,因为 NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER 的值是-1, 如果大于它, NettyWriteResponseFilter 会直接返回,就不会执行自己定义的response.大坑,在这里用@Order好像不好使, 得实现Ordered 接口
//        return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
        return -5;
    }
}

  记折腾了3天的问题