网上的代码千奇百怪,这个是我测试后整理的。
当前端请求不含body时,也能兼容。
1.读取body流进行缓存
- /**
- * 获取body请求数据(解决流不能重复读取问题)
- * 这个过滤器的order设置的是Ordered.HIGHEST_PRECEDENCE,即最高优先级的过滤器。
- * 优先级设置这么高的原因是某些系统内置的过滤器可能也会去读body,
- * 这样就会导致我们自定义过滤器中获取body的时候报body只能读取一次这样的错误
- * @author yuchen
- */
- @Slf4j
- @Component
- public class CacheRequestFilter implements GlobalFilter, Ordered {
-
- @Override
- public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
- ServerHttpRequest request = exchange.getRequest();
- if (request.getHeaders().getContentType() == null) {
- return chain.filter(exchange);
- }
- HttpMethod method = request.getMethod();
- if (method == null || method.matches("GET") || method.matches("DELETE")) {
- return chain.filter(exchange);
- }
- //当body中没有缓存时,只会执行这一个拦截器, 原因是fileMap中的代码没有执行,所以需要在波多野为空时构建一个空的缓存
- DefaultDataBufferFactory defaultDataBufferFactory = new DefaultDataBufferFactory();
- DefaultDataBuffer defaultDataBuffer = defaultDataBufferFactory.allocateBuffer(0);
- //构建新数据流, 当body为空时,构建空流
- Flux<DataBuffer> bodyDataBuffer = exchange.getRequest().getBody().defaultIfEmpty(defaultDataBuffer);
- return DataBufferUtils.join(bodyDataBuffer)
- .flatMap(dataBuffer -> {
- DataBufferUtils.retain(dataBuffer);
- Flux<DataBuffer> cachedFlux = Flux
- .defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));
- ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
- @Override
- public Flux<DataBuffer> getBody() {
- return cachedFlux;
- }
- };
- //exchange.getAttributes().put(CACHE_REQUEST_BODY_OBJECT_KEY, cachedFlux);
- return chain.filter(exchange.mutate().request(mutatedRequest).build());
- });
- }
-
- @Override
- public int getOrder() {
- return Ordered.HIGHEST_PRECEDENCE;
- }
-
- }
2.从缓存读取body,就可以重复读:
- private static String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {
- // 获取请求体
- Flux<DataBuffer> body = serverHttpRequest.getBody();
- AtomicReference<String> bodyRef = new AtomicReference<>();
- body.subscribe(buffer -> {
- CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
- DataBufferUtils.release(buffer);
- bodyRef.set(charBuffer.toString());
- });
- return bodyRef.get();
- }