我已经开始使用WebClient,我正在添加请求/响应的日志记录,并且在构造WebClient时使用filter方法:
WebClient.builder()
.baseUrl(properties.getEndpoint())
.filter((request, next) -> {
// logging
request.body()
})
.build();我可以访问url、http方法、标头,但在获取原始请求正文时遇到问题,因为请求的body()方法返回BodyInserter (BodyInserter<?, ? super ClientHttpRequest> body()。
如何将请求体的BodyInserter表示转换为String表示?或者,如何正确地记录整个请求/响应,同时还能够对其中的潜在凭据进行散列?
发布于 2020-06-18 22:39:53
您可以围绕JSON编码器创建自己的包装器/代理类,并在序列化后的正文被发送到中间管道之前对其进行拦截。
此blog post展示了如何记录WebClient请求和响应的JSON有效负载
具体地说,您应该扩展Jackson2JsonEncoder的encodeValue方法(如果是流数据,则为encodeValues )。然后,您可以随心所欲地处理这些数据,例如日志记录等。您甚至可以根据环境/配置文件有条件地执行此操作
此自定义日志编码器可以在创建WebClient时由编解码器指定:
CustomBodyLoggingEncoder bodyLoggingEncoder = new CustomBodyLoggingEncoder();
WebClient.builder()
.codecs(clientDefaultCodecsConfigurer -> {
clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonEncoder(bodyLoggingEncoder);
clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(new ObjectMapper(), MediaType.APPLICATION_JSON));
})
...更新2020/7/3:
这是一个匆忙的例子,应用了相同的原理,但对于解码器:
public class LoggingJsonDecoder extends Jackson2JsonDecoder {
private final Consumer<byte[]> payloadConsumer;
public LoggingJsonEncoder(final Consumer<byte[]> payloadConsumer) {
this.payloadConsumer = payloadConsumer;
}
@Override
public Mono<Object> decodeToMono(final Publisher<DataBuffer> input, final ResolvableType elementType, final MimeType mimeType, final Map<String, Object> hints) {
// Buffer for bytes from each published DataBuffer
final ByteArrayOutputStream payload = new ByteArrayOutputStream();
// Augment the Flux, and intercept each group of bytes buffered
final Flux<DataBuffer> interceptor = Flux.from(input)
.doOnNext(buffer -> bufferBytes(payload, buffer))
.doOnComplete(() -> payloadConsumer.accept(payload.toByteArray()));
// Return the original method, giving our augmented Publisher
return super.decodeToMono(interceptor, elementType, mimeType, hints);
}
private void bufferBytes(final ByteArrayOutputStream bao, final DataBuffer buffer) {
try {
bao.write(ByteUtils.extractBytesAndReset(buffer));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}您可以使用WebClient上的codecs构建器方法将其与编码器一起配置。当然,上面的方法只有在你的数据被反序列化为Mono的情况下才有效。但是,如果需要的话,可以覆盖其他方法。另外,我只是将生成的JSON输出到那里,但是您可以传入一个Consumer<String>或其他东西,让解码器将字符串发送到,或者只是从那里登录;由您决定。
一句警告,在它当前的形式下,这将使您的内存使用量加倍,因为它缓冲了整个响应。如果您可以立即将字节数据发送到另一个进程/线程以写入日志文件或某个输出流(甚至Flux ),则可以避免在内存中缓冲整个有效负载。
发布于 2019-07-19 17:05:27
尝试设置以下属性:
logging.level.org.springframework.web.reactive.function.client.ExchangeFunctions=TRACE
logging.level.reactor.netty.http.client.HttpClient: DEBUG
spring.http.log-request-details=true发布于 2022-02-03 22:35:04
当BodyInserter写入ReactiveHttpOutputMessage时,请求正文可能会被访问。因此,只需创建一个FilterFunction并从现有请求中创建新请求,但是对于主体设置,new BodyInserser ()覆盖方法插入请参见下面的示例。响应和请求有效负载可以被多次读取,因为它们是在DataBuffers中缓冲的
public class TracingExchangeFilterFunction implements ExchangeFilterFunction {
return next.exchange(buildTraceableRequest(request))
.flatMap(response ->
response.body(BodyExtractors.toDataBuffers())
.next()
.doOnNext(dataBuffer -> traceResponse(response, dataBuffer))
.thenReturn(response)) ;
}
private ClientRequest buildTraceableRequest(
final ClientRequest clientRequest) {
return ClientRequest.from(clientRequest).body(
new BodyInserter<>() {
@Override
public Mono<Void> insert(
final ClientHttpRequest outputMessage,
final Context context) {
return clientRequest.body().insert(
new ClientHttpRequestDecorator(outputMessage) {
@Override
public Mono<Void> writeWith(final Publisher<? extends DataBuffer> body) {
return super.writeWith(
from(body).doOnNext(buffer ->
traceRequest(clientRequest, buffer)));
}
}, context);
}
}).build();
}
private void traceRequest(ClientRequest clientRequest, DataBuffer buffer) {
final ByteBuf byteBuf = NettyDataBufferFactory.toByteBuf(buffer);
final byte[] bytes = ByteBufUtil.getBytes(byteBuf);
// do some tracing e.g. new String(bytes)
}
private void traceResponse(ClientResponse response, DataBuffer dataBuffer) {
final byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
// do some tracing e.g. new String(bytes)
}
}https://stackoverflow.com/questions/57108631
复制相似问题