Spring Gateway 同时拦截输入输出做日志操作
Spring Gateway 同时拦截输入输出做日志操作,包括request body, 和response body
主要靠代理模式,参考
https://stackoverflow.com/questions/47182961/copy-of-the-request-response-body-on-a-spring-reactive-app
需要创建 request , response, ServerWebExchangeDecorator, GlobalFilter
public class PartnerServerHttpRequestDecorator extends ServerHttpRequestDecorator {
public PartnerServerHttpRequestDecorator(ServerHttpRequest delegate) {
super(delegate);
}
private final StringBuilder cachedBody = new StringBuilder();
@Override
public Flux<DataBuffer> getBody() {
return super.getBody().doOnNext(this::cache);
}
private void cache(DataBuffer buffer) {
cachedBody.append(UTF_8.decode(buffer.asByteBuffer()));
}
public String getCachedBody() {
return cachedBody.toString();
}
}
public class PartnerServerHttpResponseDecorator extends ServerHttpResponseDecorator {
private final StringBuilder cachedBody = new StringBuilder();
public PartnerServerHttpResponseDecorator(ServerHttpResponse delegate) {
super(delegate);
}
@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 -> {
// 1.获取response中的内容
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
DataBuffer join = dataBufferFactory.join(dataBuffers);
byte[] content = new byte[join.readableByteCount()];
join.read(content);
DataBufferUtils.release(join);
String bodyStr = new String(content, StandardCharsets.UTF_8);
cachedBody.append(bodyStr);
return bufferFactory().wrap(bodyStr.getBytes());
}));
}
return super.writeWith(body);
}
public String getCachedBody() {
return cachedBody.toString();
}
}
public class PartnerServerWebExchangeDecorator extends ServerWebExchangeDecorator {
private final ServerHttpRequestDecorator requestDecorator;
private final ServerHttpResponseDecorator responseDecorator;
public PartnerServerWebExchangeDecorator(ServerWebExchange delegate) {
super(delegate);
this.requestDecorator = new PartnerServerHttpRequestDecorator(delegate.getRequest());
this.responseDecorator = new PartnerServerHttpResponseDecorator(delegate.getResponse());
}
@Override
public ServerHttpRequest getRequest() {
return requestDecorator;
}
@Override
public ServerHttpResponse getResponse() {
return responseDecorator;
}
}
然后实现GlobalFilter 即可
@Slf4j
@Component
public class OperatorLogPostFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("entry OperatorLogPostFilter");
// 使用之前声明的
PartnerServerWebExchangeDecorator partnerServerWebExchangeDecorator = new PartnerServerWebExchangeDecorator(exchange);
return chain.filter(partnerServerWebExchangeDecorator).then(Mono.fromRunnable(()-> {
// 实际返回的就是自己装饰/代理 好的类型
PartnerServerHttpRequestDecorator request = (PartnerServerHttpRequestDecorator) partnerServerWebExchangeDecorator.getRequest();
PartnerServerHttpResponseDecorator response = (PartnerServerHttpResponseDecorator) partnerServerWebExchangeDecorator.getResponse();
// 记录的操作在这里进行
log.info("request api:{},request header:{}, request body:{}, response body:{}",
request.getPath().value(),
request.getHeaders().getFirst(CommonHeader.ACCOUNT_NAME),
request.getCachedBody(),
response.getCachedBody());
}));
}
@Override
public int getOrder() {
// 这个地方非常坑,一定要小于-1才会进入到response 的代理中,因为 NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER 的值是-1, 如果大于它, NettyWriteResponseFilter 会直接返回,就不会执行自己定义的response.大坑,在这里用@Order好像不好使, 得实现Ordered 接口
// return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
return -5;
}
}
记折腾了3天的问题
posted on 2023-11-10 17:36 cococooder 阅读(217) 评论(0) 收藏 举报
浙公网安备 33010602011771号