WebFlux(reactor)中獲取完整response body數據

場景

使用Spring Cloud Gateway(SCG)時,想在網關日誌中輸出返回日誌,但由於數據流只能被讀取一次,必須使用特定方式進行重寫才能正常返回到前端。

處理過程

  • 起初使用fluxBody.map讀取數據流,會出現多次輸出的情況,由於使用的時reactor框架處理數據,導致會出現將一個結果集分爲多次處理,會執行多次map,效果不理想。
                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                    Mono<Void> newMono = super.writeWith(
                            fluxBody.map(dataBuffer -> {
                                String respBody = dataBuffer.toString(StandardCharsets.UTF_8);
                                if (respBody.length() > maxLogLength) {
                                    respBody = respBody.substring(0, maxLogLength);
                                }
                                if (PathUtil.checkPath(request.getPath().value(), contentLengthUrls)) {
                                    httpHeaders.setContentLength(respBody.length());
                                }
                                //匹配是否需要生成slat
                                if (PathUtil.checkPath(request.getPath().value(), loginUrls)) {
                                    String salt = createSlat(JSON.parseObject(respBody).getString("token"));
                                    if (StringUtils.isEmpty(salt)) {
                                        logger.warn("CreateSalt error:id={}", request.getHeaders().getFirst(HEADER_REQUEST_ID));
                                    } else {
                                        httpHeaders.add("s", salt);
                                    }
                                }
                                logger.info("fgwResponse : id={},body = {}", request.getHeaders().getFirst(HEADER_REQUEST_ID), respBody);
                                return bufferFactory.wrap(content);
                            })
                    );
                            //request中有salt則返回到response中
                            logger.info("fgwResponse : id={},resp = {}", request.getHeaders().getFirst(HEADER_REQUEST_ID), JSON.toJSONString(exchange.getResponse()));
                            return newMono;
  • 最後查看github上的討論,開發者給出DataBufferUtils.join(body) .doOnNext的方式。
@Component
public class LogRespFilter implements GlobalFilter, Ordered {

 	@Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpResponse originalResponse = exchange.getResponse();
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
            	//修改header
                HttpHeaders httpHeaders = originalResponse.getHeaders();
                httpHeaders.add("xxxxxx","aaaaaa");
                //輸出返回結果
                if (body instanceof Flux) {
                    Mono<Void> newMono = super.writeWith(
                            DataBufferUtils.join(body)
                                    .doOnNext(dataBuffer -> {
                                        String respBody = dataBuffer.toString(StandardCharsets.UTF_8);
                                        //輸出body
                                        logger.info("fgwResponse : body = {}", respBody);
                                    })
                    );
                    //輸出response,不包含body
                    logger.info("fgwResponse : resp = {}",  JSON.toJSONString(exchange.getResponse()));
                    return newMono;

				}
				return super.writeWith(body);
            }
        };
        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章