场景

使用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());

}

}

————————————————