前言
公司的开发采用了 gateway 作为微服务的网关,笔者使用中发现,gateway 网关拦截 POST 请求的参数非常麻烦,百度了很多种方法,有的获取不到,有的被读取到了,但是在往下居然获取不到了,千辛万苦终于找到了比较好的方式,以下做个笔记
相关坑
- 过滤器中的 Body 只能被读取一次,读取后之后在就读取不到了(坑爹)
- 由于异步问题,直接获取参数内容可能会为空,并且不同的 SpringBoot 版本,有的能获取到,有的获取不到(坑爹的异步)
- 由于异步问题,参数采用消费的模式,一但被读取使用后,后续就将无法再次读取到
最终处理方式
思路
- 先读取参数,并全局保存到 ServerWebExchange 中
- 读取完成后,重新构建请求,解决只能读取一次的问题
- 在下级过滤器中通过 ServerWebExchange 对象获取
代码
读取参数的过滤器
- 首先在一步回调中读取参数,防止获取不到
- 保存到 exchange 中
- 重新构造请求,防止参数失效
@Component
public class ReqPostBodyFilter implements Ordered, GlobalFilter {
@Override
public int getOrder() {
return 1;
}
@SuppressWarnings("NullableProblems")
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest req = exchange.getRequest();
ServerHttpResponse res = exchange.getResponse();
String method = req.getMethodValue();
MediaType ct = req.getHeaders().getContentType();
if (HttpMethod.POST.matches(method) {
return DataBufferUtils.join(req.getBody()).flatMap(dataBuffer -> {
byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
String bodyStr = new String(bytes, StandardCharsets.UTF_8);
exchange.getAttributes().put("POST_BODY", bodyStr);
DataBufferUtils.release(dataBuffer);
Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
DataBuffer buffer = res.bufferFactory().wrap(bytes);
return Mono.just(buffer);
});
ServerHttpRequest mutateReq = new ServerHttpRequestDecorator(req) {
@Override
public Flux<DataBuffer> getBody() {
return cachedFlux;
}
};
return chain.filter(exchange.mutate().request(mutateReq).build());
});
}
return chain.filter(exchange);
}
}
使用参数的过滤器
- 直接从全局获取
- getOrder方法的返回值必须比 读取参数的过滤器返回的数值大,否则获取不到
@Component
public class UserFilter implements Ordered, GlobalFilter {
@Autowired
private RouterWhiteList routerWhiteList;
@Override
public int getOrder() {
return 2;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String body = (String) exchange.getAttributes().get("POST_BODY");
return chain.filter(exchange);
}
}