Spring-framework: Create migration guide from RestTemplate to WebClient

Created on 10 Jul 2019  路  8Comments  路  Source: spring-projects/spring-framework

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?

web documentation

Most helpful comment

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.

All 8 comments

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.

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?...

@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
                    });
        }
    }

}

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?

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)?

Interesting approach! I think you could just as well use buf.asByteBuffer() on the DataBuffer instead of writing it to another buffer in getBody():

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.

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.

Has there been any progress on this, I could not find a guide at: https://github.com/spring-guides

Was this page helpful?
0 / 5 - 0 ratings