SpringCloud Gateway 网关的请求体body的读取和修改

SpringCloud Gateway 网关的请求体body的读取和修改

getway需要多次对body 进行操作,需要对body 进行缓存

缓存body 动态多次获取

新建顶层filter,对body 进行缓存

java 复制代码
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.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * @author: zhoumo
 * @descriptions:
 */
@Component
@Slf4j
public class RequestParamGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        /**
         * save request path and serviceId into gateway context
         */
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();

        // 处理参数
        MediaType contentType = headers.getContentType();
        long contentLength = headers.getContentLength();
        if (contentLength > 0) {
                  return readBody(exchange, chain);
        }

        return chain.filter(exchange);
    }


    /**
     * default HttpMessageReader
     */
    private static final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders();
    /**
     * ReadJsonBody
     *
     * @param exchange
     * @param chain
     * @return
     */
    private Mono<Void> readBody(ServerWebExchange exchange, GatewayFilterChain chain) {
        /**
         * join the body
         */
        return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> {
            byte[] bytes = new byte[dataBuffer.readableByteCount()];
            dataBuffer.read(bytes);
            DataBufferUtils.release(dataBuffer);
            Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
                DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
                DataBufferUtils.retain(buffer);
                return Mono.just(buffer);
            });
            /**
             * repackage ServerHttpRequest
             */
            ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
                @Override
                public Flux<DataBuffer> getBody() {
                    return cachedFlux;
                }
            };
            /**
             * mutate exchage with new ServerHttpRequest
             */
            ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build();
            /**
             * read body string with default messageReaders
             */
            return ServerRequest.create(mutatedExchange, messageReaders).bodyToMono(String.class)
                    .doOnNext(objectValue -> {
                        log.debug("[GatewayContext]Read JsonBody:{}", objectValue);
                    }).then(chain.filter(mutatedExchange));
        });
    }
    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE;
    }
}

在子节点层获取body

java 复制代码
AtomicReference<String> requestBody = new AtomicReference<>("");
                RecorderServerHttpRequestDecorator requestDecorator = new RecorderServerHttpRequestDecorator(request);
                Flux<DataBuffer> body = requestDecorator.getBody();
                body.subscribe(buffer -> {
                    CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
                    requestBody.set(charBuffer.toString());
                });

                String body= requestBody.get();

重写获取body方法

java 复制代码
   public class RecorderServerHttpRequestDecorator  extends ServerHttpRequestDecorator {
        private final List<DataBuffer> dataBuffers = new ArrayList<>();
        public RecorderServerHttpRequestDecorator(ServerHttpRequest delegate) {
            super(delegate);
            super.getBody().map(dataBuffer -> {
                dataBuffers.add(dataBuffer);
                return dataBuffer;
            }).subscribe();
        }

        @Override
        public Flux<DataBuffer> getBody() {
            return copy();
        }

        private Flux<DataBuffer> copy() {
            return Flux.fromIterable(dataBuffers)
                    .map(buf -> buf.factory().wrap(buf.asByteBuffer()));
        }
    }

对body 进行修改重新封装

java 复制代码
                String str=""+encodedDecryptedParam;
                DataBuffer bodyDataBuffer = stringBuffer(str);
                Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
                MediaType contentType = request.getHeaders().getContentType();
                ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(
                        exchange.getRequest()) {
                    @Override
                    public HttpHeaders getHeaders() {
                        HttpHeaders httpHeaders = new HttpHeaders();
                        int length = str.getBytes().length;
                        httpHeaders.putAll(super.getHeaders());
                        httpHeaders.remove(HttpHeaders.CONTENT_TYPE);
                        httpHeaders.remove(HttpHeaders.CONTENT_LENGTH);
                        httpHeaders.setContentLength(length);
                        httpHeaders.set(HttpHeaders.CONTENT_TYPE, contentType.toString());
                        // 设置CONTENT_TYPE
                        return httpHeaders;
                    }
                    @Override
                    public Flux<DataBuffer> getBody() {
                        return bodyFlux;
                    }
                };
                return chain.filter(exchange.mutate().request(mutatedRequest).build());
java 复制代码
    protected DataBuffer stringBuffer(String value) {
        byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
        NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
        DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
        buffer.write(bytes);
        return buffer;
    }

一定必须加上 public HttpHeaders getHeaders()对header 重新封装,否则接口层会卡死,request 无限大

相关推荐
上官浩仁3 小时前
springboot synchronized 本地锁入门与实战
java·spring boot·spring
不会聊天真君6474 小时前
ES(springcloud笔记第五期)
笔记·elasticsearch·spring cloud
Rysxt_6 小时前
Spring Boot Gateway 教程:从入门到精通
spring boot·网关·gateway
草履虫建模6 小时前
在 RuoYi 中接入 3D「园区驾驶舱」:Vue2 + Three.js + Nginx
运维·开发语言·javascript·spring boot·nginx·spring cloud·微服务
remaindertime6 小时前
(九)Spring Cloud Alibaba 2023.x:微服务接口文档统一管理与聚合
后端·spring cloud·微服务
Barcke6 小时前
📘 初识 WebFlux
spring boot·后端·spring
橘子在努力7 小时前
【橘子SpringCloud】OpenFegin源码分析
java·spring boot·spring·spring cloud
喂完待续7 小时前
【序列晋升】31 Spring Cloud App Broker 微服务时代的云服务代理框架
spring·spring cloud·微服务·云原生·架构·big data·序列晋升
Cyan_RA98 小时前
SpringMVC 执行流程分析 详解(图解SpringMVC执行流程)
java·人工智能·后端·spring·mvc·ssm·springmvc
索迪迈科技8 小时前
Java-Spring入门指南(四)深入IOC本质与依赖注入(DI)实战
java·开发语言·spring