spring-cloud-gateway獲取post請求body參數以及響應數據


@Component
@Slf4j
@AllArgsConstructor
public class HttpPostBodyFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); String method = request.getMethodValue(); String contentType = request.getHeaders().getFirst("Content-Type"); if ("POST".equals(method) && contentType.startsWith("multipart/form-data")){ return DataBufferUtils.join(exchange.getRequest().getBody()) .flatMap(dataBuffer -> { byte[] bytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bytes); try { String bodyString = new String(bytes, "utf-8"); log.info(bodyString); exchange.getAttributes().put("POST_BODY",bodyString); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } DataBufferUtils.release(dataBuffer); Flux<DataBuffer> cachedFlux = Flux.defer(() -> { DataBuffer buffer = exchange.getResponse().bufferFactory() .wrap(bytes); return Mono.just(buffer); }); ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator( exchange.getRequest()) { @Override public Flux<DataBuffer> getBody() { return cachedFlux; } }; return chain.filter(exchange.mutate().request(mutatedRequest) .build()); }); } return chain.filter(exchange); } @Override public int getOrder() { return -200; } }

 

主要思路就是在優先級最高的過濾器里面,CacheBodyGlobalFilter這個全局過濾器的目的就是把原有的request請求中的body內容讀出來,並且使用ServerHttpRequestDecorator這個請求裝飾器對request進行包裝,重寫getBody方法,並把包裝后的請求放到過濾器鏈中傳遞下去。這樣后面的過濾器中再使用exchange.getRequest().getBody()來獲取body時,實際上就是調用的重載后的getBody方法,獲取的最先已經緩存了的body數據。這樣就能夠實現body的多次讀取了。

過濾器優先級不一定是最高,但是要在要獲取body之前執行,然后后面在身份鑒定的等過濾器里面,獲取到body

@Component
@Slf4j
public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public int getOrder() {
        return -2;
    }
 
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        ServerHttpResponse originalResponse = exchange.getResponse();
        //如果是post請求,將請求體取出來,再寫入
        HttpMethod method = serverHttpRequest.getMethod();
        //請求參數,post從請求里獲取請求體
        String requestBodyStr = HttpMethod.POST.equals(method) ? resolveBodyFromRequest(serverHttpRequest) : null;
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                    return super.writeWith(fluxBody.buffer().map(dataBuffers -> {//解決返回體分段傳輸
                        StringBuffer stringBuffer = new StringBuffer();
                        dataBuffers.forEach(dataBuffer -> {
                            byte[] content = new byte[dataBuffer.readableByteCount()];
                            dataBuffer.read(content);
                            DataBufferUtils.release(dataBuffer);
                            try {
                                stringBuffer.append(new String(content, "utf-8"));
                            } catch (Exception e) {
                                log.error("--list.add--error", e);
                            }
                        });
                        String result = stringBuffer.toString();
                        //TODO,result就是response的值,想修改、查看就隨意而為了
                        String url = serverHttpRequest.getPath().toString();
                        String urlParams = UrlUtil.getParamsByMap(serverHttpRequest.getQueryParams().toSingleValueMap());
 
                        JSONObject jsonObject = JSONObject.parseObject(result);
                        log.info("請求長度:" + StringUtils.length(requestBodyStr) + ",返回data長度:" + StringUtils.length(jsonObject.getString("data")));
                        log.info("請求地址:【{}】請求參數:GET【{}】|POST:【\n{}\n】,響應數據:【\n{}\n】", url, urlParams, requestBodyStr, result);
 
                        byte[] uppedContent = new String(result.getBytes(), Charset.forName("UTF-8")).getBytes();
                        originalResponse.getHeaders().setContentLength(uppedContent.length);
                        return bufferFactory.wrap(uppedContent);
                    }));
                }
                // if body is not a flux. never got there.
                return super.writeWith(body);
            }
        };
        // replace response with decorator
        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }
 
    /**
     * 從Flux<DataBuffer>中獲取字符串的方法
     *
     * @return 請求體
     */
    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());
        });
        //獲取request body
        return bodyRef.get();
    }
}

這里可以把請求地址、參數、body、響應數據一起打印出來,測試的post請求

請求參數有56.97kb,后端order-center的接收打印,把整個數據完全接收並返回出去

網關的打印請求參數數據

再來一個get請求的

其他的put、delete等請求均試過正常請求

用postman的壓測結果和對比,數據都完全正常,並且能通過json格式化,說明數據格式也保持了一致

代碼還有很多可以優化改進的地方,根據自己的也無需求來,比如簽名、token等統一校驗處理


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM