The RestTemplate will be deprecated in a future version and will not have major new features added going forward. https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html
Could you add a detailed migration guide on how to refactor existing RestTemplate
(which typically is for sync blocking requests) into WebClient
requests (also sync and blocking)?
Because the usual case for WebClient is probably for async requests, but chances are high that if someone refactors to WebClient
he want's to stick to his synchronous patterns.
Wouldn't this be a good idea to grab spring users by their hands just now, if it's planned to deprecate RestTemplate
in future anyhow?
Comment From: rstoyanchev
That is a good idea to put together such a a resource for those only familiar with the RestTemplate
and simply concerned about its potential deprecation, and not necessarily familiar or even interested in the reactive features.
Note that there is a section in the reference on synchronous use of the WebClient
, although we seem to be missing links to it from the content on the RestTemplate
. Simply using the WebClient
with block is quite straight forward, so definitely read that and give it a try.
Beyond that even if not interested in "reactive", any time you need to do more than one call, there is a good chance simply blocking your way out of each call is missing the power of the WebClient
as well as how you can use it in Spring MVC. So I would encourage reading up on the WebClient
and learning how to compose multiple calls with it. You can also have a look at this talk of mine which is roughly in that area and starts with RestTemplate.
Comment From: membersound
If you'll be extending the documentations, maybe you could also go into detail on how to properly log http message body for both request and response in case of sync calls?
For example in RestTemplate
it was as easy as adding a ClientHttpRequestInterceptor
that just logs the bodies. Together with a BufferingClientHttpRequestFactory
is was simply possible to read (log) the body, and then continue the normal flow with the response body in memory.
Yet I did not succeed creating the same for a WebClient, if possible at all?...
Comment From: rstoyanchev
@membersound, good question. You'd probably need to wrap at the connector in order to get to the lower level ClientHttpResponse
which then allows you to intercept the body. I haven't tested this but something like this:
public class LoggingConnector implements ClientHttpConnector {
private final ClientHttpConnector delegate;
public LoggingConnector(ClientHttpConnector connector) {
this.delegate = connector;
}
@Override
public Mono<ClientHttpResponse> connect(HttpMethod method, URI uri,
Function<? super ClientHttpRequest, Mono<Void>> requestCallback) {
return this.delegate.connect(method, uri, requestCallback).map(LoggingResponse::new);
}
private static class LoggingResponse extends ClientHttpResponseDecorator {
private static final DataBufferFactory bufferFactory = new DefaultDataBufferFactory();
private final DataBuffer buffer = bufferFactory.allocateBuffer();
LoggingResponse(ClientHttpResponse delegate) {
super(delegate);
}
@Override
public Flux<DataBuffer> getBody() {
return super.getBody()
.doOnNext(this.buffer::write)
.doOnComplete(() -> {
// Log buffer
});
}
}
}
Comment From: membersound
Interesting approach! I think you could just as well use buf.asByteBuffer()
on the DataBuffer
instead of writing it to another buffer in getBody()
:
.doOnNext(buf -> LOGGER.info(StandardCharsets.UTF_8.decode(buf.asByteBuffer()).toString()))
It would be a plus if one could also access the ClientResponse
somehow inside getBody(), so one could log both http headers + body in one place.
Do you have a further idea how the request-body could also be intercepted within the same connector?
Comment From: membersound
Would the following be correct?
private class LoggingRequest extends ClientHttpRequestDecorator {
public LoggingRequest(ClientHttpRequest delegate) {
super(delegate);
}
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
return DataBufferUtils.join((Publisher<DataBuffer>) body).flatMap(buf -> {
LOGGER.info(StandardCharsets.UTF_8.decode(buf.asByteBuffer()).toString());
return super.writeWith(Mono.fromSupplier(() -> buf));
});
}
}
Or would I also have to override writeAndFlushWith()
(how)?
Comment From: membersound
Interesting approach! I think you could just as well use
buf.asByteBuffer()
on theDataBuffer
instead of writing it to another buffer ingetBody()
:
To correct myself: no, it's not possible to directly write the buffer out. Because the payload may be chunked and then also log chunked content. Instead, collect it inside another DataBuffer
and log it on complete is correct.
But I'm using .doOnTerminate()
instead of onComplete()
to also log error responses.
Comment From: rstoyanchev
Team Decision: This is a good idea, it needs to be done. It won't be part of the Spring Framework reference -- to avoid bloating it and also because such a guide would go beyond this projects (e.g. some parts would relate to Boot). It should rather be an independent guide, perhaps under https://github.com/spring-guides, but it makes sense to leave the ticket here for now.
Comment From: billNaylor
Has there been any progress on this, I could not find a guide at: https://github.com/spring-guides
Comment From: ingogriebsch
I would like to repeat @billNaylor question... Has there been any progress on this? It would be really helpful to understand in more detail how to migrate from a RestTemplate to a WebClient (especially if not switching from an imperative stack to a reactive stack).
Comment From: anbusampath
With Introduction of RestClient , we should be looking for RestTemplate to RestClient migration guide, since RestClient uses same underlying RestTemplate infrastructure.