Skip to content

HTTP interface client infrastructure and adapter for RestTemplate #30117

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
ooraini opened this issue Mar 14, 2023 · 2 comments
Closed

HTTP interface client infrastructure and adapter for RestTemplate #30117

ooraini opened this issue Mar 14, 2023 · 2 comments
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement
Milestone

Comments

@ooraini
Copy link

ooraini commented Mar 14, 2023

Affects: <Spring Framework version>


The new HTTP interface clients only supports WebClient, for MVC users this means an additional dependency on web-flux and dealing with Spring boot auto configuration for web-flux.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Mar 14, 2023
@rstoyanchev rstoyanchev added in: web Issues in web modules (web, webmvc, webflux, websocket) and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Mar 14, 2023
@rstoyanchev
Copy link
Contributor

rstoyanchev commented Mar 14, 2023

hi @ooraini, thanks for bringing this up.

HttpClientAdapter can be implemented quite easily with the RestTemplate. For example:

RestTemplateAdapter

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.service.invoker.HttpClientAdapter;
import org.springframework.web.service.invoker.HttpRequestValues;


public class RestTemplateAdapter implements HttpClientAdapter {

	private final RestTemplate restTemplate;


	public RestTemplateAdapter(RestTemplate restTemplate) {
		this.restTemplate = restTemplate;
	}


	@Override
	public Mono<Void> requestToVoid(HttpRequestValues values) {
		this.restTemplate.exchange(newRequest(values), Void.class);
		return Mono.empty();
	}

	@Override
	public Mono<HttpHeaders> requestToHeaders(HttpRequestValues values) {
		HttpHeaders headers = this.restTemplate.exchange(newRequest(values), Void.class).getHeaders();
		return Mono.just(headers);
	}

	@Override
	public <T> Mono<T> requestToBody(HttpRequestValues values, ParameterizedTypeReference<T> type) {
		T body = this.restTemplate.exchange(newRequest(values), type).getBody();
		return Mono.justOrEmpty(body);
	}

	@Override
	public <T> Flux<T> requestToBodyFlux(HttpRequestValues values, ParameterizedTypeReference<T> type) {
		throw new UnsupportedOperationException("Not supported with RestTemplate");
	}

	@Override
	public Mono<ResponseEntity<Void>> requestToBodilessEntity(HttpRequestValues values) {
		ResponseEntity<Void> entity = this.restTemplate.exchange(newRequest(values), Void.class);
		return Mono.just(entity);
	}

	@Override
	public <T> Mono<ResponseEntity<T>> requestToEntity(HttpRequestValues values, ParameterizedTypeReference<T> type) {
		ResponseEntity<T> entity = this.restTemplate.exchange(newRequest(values), type);
		return Mono.just(entity);
	}

	@Override
	public <T> Mono<ResponseEntity<Flux<T>>> requestToEntityFlux(HttpRequestValues values, ParameterizedTypeReference<T> type) {
		throw new UnsupportedOperationException("Not supported with RestTemplate");
	}

	@SuppressWarnings("ReactiveStreamsUnusedPublisher")
	private RequestEntity<?> newRequest(HttpRequestValues values) {

		HttpMethod httpMethod = values.getHttpMethod();
		Assert.notNull(httpMethod, "HttpMethod is required");

		RequestEntity.BodyBuilder builder;

		if (values.getUri() != null) {
			builder = RequestEntity.method(httpMethod, values.getUri());
		}
		else if (values.getUriTemplate() != null) {
			builder = RequestEntity.method(httpMethod, values.getUriTemplate(), values.getUriVariables());
		}
		else {
			throw new IllegalStateException("Neither full URL nor URI template");
		}

		builder.headers(headers -> headers.putAll(values.getHeaders()));
		// TODO: cookies

		if (values.getBodyValue() != null) {
			builder.body(values.getBodyValue());
		}
		else if (values.getBody() != null) {
			throw new IllegalArgumentException("Publisher body is not supported");
		}

		return builder.build();
	}

}

Mainly, it's the Flux methods that are excluded as those cannot be supported with the RestTemplate, and likewise, it's not an option to use Flux or Mono as inputs in @HttpExchange methods either. Aside from that, the rest should work.

There are a couple of things we can consider as part of this request:

  1. Providing an implementation like the one above.
  2. Supporting an alternative HttpClientAdapter contract that does not depend on Reactor.

The work for #29552 should also help to inform decisions we make here. In particular of interest is whether a Loom-friendly contract would support asynchronous requests, and streaming with decoding to objects.

@rstoyanchev rstoyanchev added this to the 6.x Backlog milestone Mar 14, 2023
@rstoyanchev rstoyanchev added the type: enhancement A general enhancement label Mar 14, 2023
@rstoyanchev rstoyanchev self-assigned this Mar 27, 2023
@rstoyanchev rstoyanchev removed their assignment Apr 24, 2023
@rstoyanchev rstoyanchev modified the milestones: 6.x Backlog, 6.1.0-M2 Apr 24, 2023
@OlgaMaciaszek
Copy link
Contributor

We are going to experiment with this for 6.1.

@rstoyanchev rstoyanchev self-assigned this Jun 19, 2023
rstoyanchev added a commit that referenced this issue Jul 11, 2023
rstoyanchev added a commit that referenced this issue Jul 11, 2023
Now that HttpClientAdapter is deprecated and replaced by HttpExchangeAdapter
and ReactorHttpExchangeAdapter, our tests should use the new contracts.

See gh-30117
rstoyanchev added a commit that referenced this issue Jul 11, 2023
rstoyanchev added a commit that referenced this issue Jul 11, 2023
HTTP interface client argument resolvers for RequestBody and
RequestPart now handle reactive input conditionally.

See gh-30117
@rstoyanchev rstoyanchev changed the title HttpClientAdapter for RestTemplate HTTP interface client infrastructure and adapter for RestTemplate Jul 11, 2023
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

4 participants