獲取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