当前位置: 首页 > news >正文

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

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

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

缓存body 动态多次获取

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


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 {@Overridepublic 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()) {@Overridepublic 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));});}@Overridepublic int getOrder() {return HIGHEST_PRECEDENCE;}
}

在子节点层获取body

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方法

   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();}@Overridepublic Flux<DataBuffer> getBody() {return copy();}private Flux<DataBuffer> copy() {return Flux.fromIterable(dataBuffers).map(buf -> buf.factory().wrap(buf.asByteBuffer()));}}

对body 进行修改重新封装

                String str=""+encodedDecryptedParam;DataBuffer bodyDataBuffer = stringBuffer(str);Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);MediaType contentType = request.getHeaders().getContentType();ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {@Overridepublic 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_TYPEreturn httpHeaders;}@Overridepublic Flux<DataBuffer> getBody() {return bodyFlux;}};return chain.filter(exchange.mutate().request(mutatedRequest).build());
    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 无限大

相关文章:

  • SpringCloud Alibaba Demo(Nacos,OpenFeign,Gatway,Sentinel)
  • 下载的nginx证书转换成tomcat证书格式
  • 基于架构的软件开发方法
  • 访问后端接口报错:net::ERR_CONNECTION_RESET
  • SpringCloud(六) Nacos配置管理
  • 延时摄影视频制作工具 LRTimelapse mac中文版特点介绍
  • python 实时读取文件数据生成折线图——Matplotlib
  • Spring Boot 使用断言抛出自定义异常,优化异常处理机制
  • okhttp post请求 header post参数加密遇到的两个问题
  • 怎么让重要文件自动备份到OneDrive?
  • 【QT】基本的绘图操作和高级绘图
  • 算法:查找数组中第K大的元素
  • ONNX的结构与转换
  • 数仓建模—数仓建设概论
  • js替换字符串中的某个字符
  • 30天自制操作系统-2
  • CentOS从零开始部署Nodejs项目
  • java 多线程基础, 我觉得还是有必要看看的
  • node 版本过低
  • Python 基础起步 (十) 什么叫函数?
  • Synchronized 关键字使用、底层原理、JDK1.6 之后的底层优化以及 和ReenTrantLock 的对比...
  • tab.js分享及浏览器兼容性问题汇总
  • Zepto.js源码学习之二
  • 基于遗传算法的优化问题求解
  • 技术发展面试
  • 聚簇索引和非聚簇索引
  • 前端技术周刊 2019-01-14:客户端存储
  • 巧用 TypeScript (一)
  • 使用 Docker 部署 Spring Boot项目
  • 使用agvtool更改app version/build
  • 推荐一个React的管理后台框架
  • 一些css基础学习笔记
  • [地铁译]使用SSD缓存应用数据——Moneta项目: 低成本优化的下一代EVCache ...
  • mysql 慢查询分析工具:pt-query-digest 在mac 上的安装使用 ...
  • scrapy中间件源码分析及常用中间件大全
  • 阿里云重庆大学大数据训练营落地分享
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • (done) 两个矩阵 “相似” 是什么意思?
  • (Redis使用系列) SpringBoot中Redis的RedisConfig 二
  • (附源码)ssm基于jsp高校选课系统 毕业设计 291627
  • (汇总)os模块以及shutil模块对文件的操作
  • (剑指Offer)面试题34:丑数
  • (一) storm的集群安装与配置
  • (一)python发送HTTP 请求的两种方式(get和post )
  • (原創) 如何刪除Windows Live Writer留在本機的文章? (Web) (Windows Live Writer)
  • ***微信公众号支付+微信H5支付+微信扫码支付+小程序支付+APP微信支付解决方案总结...
  • .NET C#版本和.NET版本以及VS版本的对应关系
  • .net CHARTING图表控件下载地址
  • .Net Core/.Net6/.Net8 ,启动配置/Program.cs 配置
  • .Net Winform开发笔记(一)
  • .NET(C#) Internals: as a developer, .net framework in my eyes
  • .NET简谈互操作(五:基础知识之Dynamic平台调用)
  • .NET下的多线程编程—1-线程机制概述
  • ?
  • @Async注解的坑,小心