网上的代码千奇百怪,这个是我测试后整理的。
当前端请求不含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();
}