获取spring cloud gateway POST请求体的时候,会有不少坑,网上大多数解决方案是html
/** 这种方法在spring-boot-starter-parent 2.0.6.RELEASE + Spring Cloud Finchley.SR2 body 中生效, 可是在spring-boot-starter-parent 2.1.0.RELEASE + Spring Cloud Greenwich.M3 body 中不生效,老是为空 */ private 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(); }
可是实际这种解决方案(例如 这篇文章)会带来不少问题,好比request不能在其余filter中获取,会报错:java
reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.IllegalStateException: Only one connection receive subscriber allowed. Caused by: java.lang.IllegalStateException: Only one connection receive subscriber allowed.
针对这种不能重复获取的问题,网上通用解决是把request从新包装,继续传递,好比 这篇文章的解决方案。
可是这种方案还会带来request body获取不完整,只能获取1024B的数据,这个问题暂时没有很好的解法,很头痛,在给官方提issues的时候,issues709 和issues707 的时候,对方让我参看一个类ModifyRequestBodyGatewayFilterFactory.java,说真的并无看懂,最后翻源码的时候,发现了一个预言类,ReadBodyPredicateFactory ,发现里面缓存了request body的信息,因而在自定义router中配置了ReadBodyPredicateFactory,而后在filter中经过cachedRequestBodyObject缓存字段获取request body信息,这种解决,一不会带来重复读取问题,二不会带来requestbody取不全问题。三在低版本的Spring Cloud Finchley.SR2也能够运行。react
step 1:如今自动以router里面配置ReadBodyPredicate预言类: RouteLocatorBuilder.Builder serviceProvider = builder. routes().route("gateway-sample", r -> r.readBody(Object.class, requestBody -> { log.info("requestBody is {}", requestBody); // 这里不对body作判断处理 return true; }).and().path("/service"). filters(f -> { f.filter(requestFilter); return f; }) .uri("http://127.0.0.1:8009")); RouteLocator routeLocator = serviceProvider.build();
step2:在自定义filter中获取缓存了的request body: Object requestBody = exchange.getAttribute("cachedRequestBodyObject");
至此问题解决,完整代码在个人github上面。参考这里。git
参考:
https://www.cnblogs.com/cafebabe-yun/p/9328554.html
http://www.javashuo.com/article/p-gbpdhfpn-ea.htmlgithub