Springcloudgateway如何在全局过滤器中获得请求体和响应体

需要使用请求装饰类和响应装饰类,把请求体和响应体保存一下,再在全局Post过滤器里面获得该请求体。

请求装饰类

java 复制代码
package com.chilun.apiopenspace.gateway.filter;

import com.chilun.apiopenspace.gateway.Utils.LogCacheMap;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;

import java.nio.charset.StandardCharsets;

/**
 * @author 齿轮
 * @date 2024-02-29-17:51
 */
@Slf4j
public class LoggingRequestDecorator extends ServerHttpRequestDecorator {
    private final String uuid;
    private final DataBufferFactory bufferFactory;

    public LoggingRequestDecorator(ServerHttpRequest delegate, ServerWebExchange exchange, String uuid) {
        super(delegate);
        this.bufferFactory = exchange.getResponse().bufferFactory();
        this.uuid = uuid;
    }

    @NotNull
    @Override
    public Flux<DataBuffer> getBody() {
        Flux<DataBuffer> originalBody = super.getBody();
        return originalBody.map(dataBuffer -> {
            byte[] content = new byte[dataBuffer.readableByteCount()];
            dataBuffer.read(content);
            // 将请求体数据转换为字符串,并保存到缓存中
            String requestBody = new String(content, StandardCharsets.UTF_8);
            log.info("Request Body: " + requestBody);
            LogCacheMap.saveRequest(uuid, requestBody);
            //重新包装了DataBuffer,所以要释放原始的DataBuffer
            DataBufferUtils.release(dataBuffer);
            // 返回新的DataBuffer
            return bufferFactory.wrap(content);
        });
    }
}

响应装饰类

java 复制代码
package com.chilun.apiopenspace.gateway.filter;

import com.chilun.apiopenspace.gateway.Utils.LogCacheMap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

/**
 * @author 齿轮
 * @date 2024-02-29-17:51
 */
@Slf4j
public class LoggingResponseDecorator extends ServerHttpResponseDecorator {
    private final DataBufferFactory bufferFactory;
    private final String uuid;

    public LoggingResponseDecorator(ServerHttpResponse delegate, String uuid) {
        super(delegate);
        this.bufferFactory = delegate.bufferFactory();
        this.uuid = uuid;
    }

    @Override
    public Mono<Void> writeWith(org.reactivestreams.Publisher<? extends org.springframework.core.io.buffer.DataBuffer> body) {
//        if (getStatusCode().is2xxSuccessful()) {
//            //正常情况直接返回
//            return super.writeWith(body);
//        } else 
            if (body instanceof Flux) {
            //异常情况保存响应
            Flux<? extends DataBuffer> fluxBody = Flux.from(body);
            return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
                        DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
                        DataBuffer join = dataBufferFactory.join(dataBuffers);
                        byte[] content = new byte[join.readableByteCount()];
                        join.read(content);
                        DataBufferUtils.release(join);
                        String responseBody = new String(content, StandardCharsets.UTF_8);
                        log.info("response body: {}", responseBody);
                        LogCacheMap.saveResponse(uuid, responseBody);
                        byte[] uppedContent = responseBody.replaceAll(":null", ":\"\"").getBytes(StandardCharsets.UTF_8);
                        return bufferFactory.wrap(uppedContent);
                    }
            ));
        }
        return super.writeWith(body);
    }
}

用到的自定义工具类

java 复制代码
package com.chilun.apiopenspace.gateway.Utils;


import java.util.HashMap;

/**
 * @author 齿轮
 * @date 2024-02-29-18:52
 */

public class LogCacheMap {
    public static HashMap<String, String> LogMap = new HashMap<>();

    public static void saveRequest(String uuid, String request) {
        LogMap.put(uuid + "request", request);
    }

    public static String getRequest(String uuid) {
        return LogMap.remove(uuid + "request");
    }

    public static void saveResponse(String uuid, String response) {
        LogMap.put(uuid + "response", response);
    }

    public static String getResponse(String uuid) {
        return LogMap.remove(uuid + "response");
    }
}

全局过滤器类

java 复制代码
package com.chilun.apiopenspace.gateway.filter;

import com.chilun.apiopenspace.gateway.Utils.LogCacheMap;
import com.chilun.apiopenspace.gateway.service.AccessLogService;
import com.google.common.base.Joiner;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.UUID;

/**
 * @author 齿轮
 * @date 2024-02-28-14:35
 */
@Slf4j
@Component
public class LogFilter implements GlobalFilter, Ordered {
    @Resource
    AccessLogService logService;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String uuid = UUID.randomUUID().toString();

        //注入requestDecorator、responseDecorator用于获得请求体、响应体
        ServerHttpRequestDecorator requestDecorator = new LoggingRequestDecorator(exchange.getRequest(), exchange, uuid);
        ServerHttpResponseDecorator responseDecorator = new LoggingResponseDecorator(exchange.getResponse(), uuid);
        return chain.filter(exchange.mutate().request(requestDecorator).response(responseDecorator).build()).then(Mono.just(exchange))
                .map(serverWebExchange -> {
                    if (serverWebExchange.getResponse().getStatusCode().is2xxSuccessful()) {
                        logService.sendCommonLog(true);
                        logService.sendRightLog(LogCacheMap.getRequest(uuid), LogCacheMap.getResponse(uuid));

                    } else {
                        logService.sendCommonLog(false);
                        logService.sendErrorLog(LogCacheMap.getRequest(uuid), LogCacheMap.getResponse(uuid));
                    }
                    return serverWebExchange;
                })
                .then();
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

如果也是使用Map存储请求体,注意用不到请求体/响应体后删除他们,避免内存溢出。

相关推荐
码农飞哥1 小时前
互联网大厂Java面试实战:Spring Boot到微服务的技术问答解析
java·数据库·spring boot·缓存·微服务·消息队列·面试技巧
Auc241 小时前
OJ判题系统第4期之判题机模块架构——设计思路、实现步骤、代码实现(工厂模式、代理模式的实践)
java·spring cloud·log4j·mybatis·代理模式·工厂模式
掘金-我是哪吒2 小时前
分布式微服务系统架构第126集:集群,数据库扩展,多节点分布,分库,分表,分片,分表,运维
运维·数据库·分布式·微服务·系统架构
zm2 小时前
网络编程epoll和udp
服务器·网络·数据库
张一不吃豆芽2 小时前
TCPIP详解 卷1协议 八 ICMPv4和ICMPv6 Internet控制报文协议
网络·网络协议·tcp/ip
是垚不是土3 小时前
探秘高可用负载均衡集群:企业网络架构的稳固基石
运维·服务器·网络·云原生·容器·架构·负载均衡
向哆哆4 小时前
Netty在Java网络编程中的应用:实现高性能的异步通信
java·网络·php
大G哥4 小时前
实战演练:用 AWS Lambda 和 API Gateway 构建你的第一个 Serverless API
云原生·serverless·云计算·gateway·aws
@西瓜@4 小时前
(网络)应用层协议-HTTPS
网络·网络协议·https
海尔辛4 小时前
学习黑客5 分钟读懂什么是 CVE?
网络·学习·安全