SpringCloudGateway 过滤器获取application/json中body数据
一、前言
???项目接口需要加解密,就在网关层进行解密操作。那么问题来了怎么在gateway 的filter 中获取 body(application/json)中的数据呢? 经过一顿百度一顿验证发现了两种方式
- 一种是通过cachedRequestBodyObject缓存获取request body信息
- 一种是通过ServerHttpRequest.getBody方法获取body信息
二、通过cachedRequestBodyObject缓存获取
通过cachedRequestBodyObject 获取的话 路由配置需要使用java方式配置
ReadBodyPredicateFactory里面缓存了request body的信息,于是在自定义router中配置了ReadBodyPredicateFactory,然后在filter中通过cachedRequestBodyObject缓存字段获取request body信息。
路由配置
@EnableAutoConfiguration
@Configuration
public class GatewayRouteConfig {
@Resource
private RequestParamDecryptFilter2 paramDecryptFilter;
private static final String PATH_URL = "/UserCenter/**";
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route(r -> r
.header(HttpHeaders.CONTENT_TYPE,
MediaType.APPLICATION_JSON_VALUE + MediaType.APPLICATION_JSON_UTF8_VALUE)
.and()
.method(HttpMethod.POST)
.and()
.readBody(Object.class, readBody -> {
return true;
})
.and()
.path(PATH_URL)
.filters(f -> {
f.filter(paramDecryptFilter);
return f;
})
.uri("http://127.0.0.1:8083")).build();
}
}
自定义filter
@Slf4j
@Component
public class RequestParamDecryptFilter2 implements GatewayFilter, Ordered {
@Override
public int getOrder() {
return 0;
}
private static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
Object cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY);
if (null != cachedBody) {
String bodyStr = cachedBody.toString();
}
return chain.filter(exchange);
}
}
三、ServerHttpRequest getBody方法获取
通过gateBody()方法获取 需要两个filter
- 第一个filter 进行ServerHttpRequest getBody方法的重写
- 第二个filter 解析body
ServerHttpRequest getBody 重写filter
@Slf4j
@Component
public class ServerHttpReqFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
return HIGHEST_PRECEDENCE;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
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);
});
ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
return cachedFlux;
}
};
ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build();
return ServerRequest.create(mutatedExchange, HandlerStrategies.withDefaults().messageReaders()).bodyToMono(String.class)
.doOnNext(objectValue -> {
}).then(chain.filter(mutatedExchange));
});
}
}
真正获取body 数据进行 业务逻辑处理的filter
@Slf4j
@Component
public class RequestParamDecryptFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
return 0;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
log.info("request path :{}", request.getPath());
StringBuffer paramBuffer = new StringBuffer();
Flux<DataBuffer> body = exchange.getRequest().getBody();
body.subscribe(buffer -> {
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
log.info("request charBuffer:{}", charBuffer);
paramBuffer.append(charBuffer);
});
String param = paramBuffer.toString();
log.info("request param :{}", paramBuffer.toString());
DataBuffer bodyDataBuffer = stringBuffer(param);
Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
request = new ServerHttpRequestDecorator(request) {
@Override
public Flux<DataBuffer> getBody() {
return bodyFlux;
}
};
return chain.filter(exchange.mutate().request(request).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;
}
}
那么为什么要用两个filter?
StringBuffer paramBuffer = new StringBuffer();
Flux<DataBuffer> body = exchange.getRequest().getBody();
body.subscribe(buffer -> {
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
log.info("request charBuffer:{}", charBuffer);
paramBuffer.append(charBuffer);
});
String param = paramBuffer.toString();
??????在以上代码中,当你直接通过body.subscribe 解析数据的时候,你会发现他在filter执行完才会执行body.subscribe()方法内的内容(debug可以试试 在filter执行完才会debug到里边)。 ??????SpringCloudGateWay 是底层是 Spring webflux 是非阻塞线程的,所以当你直接通过body.subscribe() 来解析的话 他还没执行 你就直接用 paramBuffer 来 这个时候获取的是null 所以需要重写 getBody方法。
四、(* ̄︶ ̄)
如果还有其他好的获取方法, 请评论告知一下,共同学习,共同进步。谢谢啦
如果对你有帮助,加个关注把~
|