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存储请求体,注意用不到请求体/响应体后删除他们,避免内存溢出。

相关推荐
桌面运维家5 小时前
IDV云桌面vDisk机房网络管控访问限制部署方案
运维·服务器·网络
橙子也要努力变强7 小时前
Linux信号机制
linux·服务器·网络
程序猿编码7 小时前
给你的网络流量穿件“隐形衣“:手把手教你用对称加密打造透明安全隧道
linux·开发语言·网络·安全·linux内核
skilllite作者7 小时前
AI agent 的 Assistant Auto LLM Routing 规划的思考
网络·人工智能·算法·rust·openclaw·agentskills
pengyi8710158 小时前
私网IP映射公网基础原理,搭配代理IP远程访问入门
linux·服务器·网络
AILabNotes8 小时前
014、隐私增强技术:零知识证明与混合网络在网关中的应用
网络·区块链·零知识证明
fanly1110 小时前
AI 赋能微服务工程化:Surging Engine-CLI 的插件化 Agent 架构革新
微服务·surging
Y学院12 小时前
网络安全基础核心知识点教程
网络·web安全·php
风曦Kisaki13 小时前
#企业级网络架构Day01:网络概述,网络参考模型,交换机命令行
网络·架构·智能路由器
大熊背13 小时前
Serial over TCP实现原理
网络·tcp·isppipeline