Skip to content

Support dynamic default uri variables for RestClient #34190

New issue

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

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

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

quaff
Copy link
Contributor

@quaff quaff commented Jan 3, 2025

Closes GH-34189

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jan 3, 2025
@bclozel
Copy link
Member

bclozel commented Jan 6, 2025

I'm declining this PR because I don't think we should take this approach to solve this problem.

The UriComponents infrastructure is shared for both RestClient and WebClient. They're both declaring defaultUriVariables(Map<String, ?> defaultUriVariables) methods that provide static values as a Map for the entire lifetime of the client. Supporting Function or Supplier means that such values will be calculated at runtime, possibly depending on the execution context. The background information in #34189 shows MDC usage which is a typical example of this. This will create concurrency and lifecycle problems as URLs might be built on a different thread.

I think this use case should be supported by ExchangeFilterFunction + reactor context for WebClient, and ClientHttpRequestInterceptor + request attributes for RestTemplate and RestClient.

@bclozel bclozel closed this Jan 6, 2025
@bclozel bclozel added in: web Issues in web modules (web, webmvc, webflux, websocket) status: declined A suggestion or change that we don't feel we should currently apply and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Jan 6, 2025
@quaff
Copy link
Contributor Author

quaff commented Jan 7, 2025

This will create concurrency and lifecycle problems as URLs might be built on a different thread.

It's safe for RestClient and RestTemplate, It may be a problem for WebClient, but it's the developer's choice to or not to use Supplier/Function as default variable value.

I think this use case should be supported by ExchangeFilterFunction + reactor context for WebClient, and ClientHttpRequestInterceptor + request attributes for RestTemplate and RestClient.

Could you reopen #34189 or create new issue to address it officially?

@quaff
Copy link
Contributor Author

quaff commented Jan 7, 2025

Another thought, introduce a subclass of UriComponentsBuilder to accept Supplier/Function, let application construct their own DefaultUriBuilderFactory, for example:

UriComponentsBuilder ucb = UriComponentsBuilder.fromUriString("http://{partition}.163.com").acceptFunctions();
DefaultUriBuilderFactory  uriBuilderFactory = new DefaultUriBuilderFactory(ucb);
uriBuilderFactory.setDefaultUriVariables(defaultUriVariables);
RestClient restClient = RestClient.builder().uriBuilderFactory(uriBuilderFactory).build();

WDYT @bclozel

@bclozel
Copy link
Member

bclozel commented Jan 7, 2025

Could you reopen #34189 or create new issue to address it officially?

I meant that this should be already possible with an interceptor and a better for for this use case.

@quaff
Copy link
Contributor Author

quaff commented Jan 7, 2025

Could you reopen #34189 or create new issue to address it officially?

I meant that this should be already possible with an interceptor and a better for for this use case.

I tried with ClientHttpRequestInterceptor:

		RestClient restClient = RestClient.builder().baseUrl("http://{partition}.example.com")
				.requestInterceptor(new ClientHttpRequestInterceptor() {
					@Override
					public ClientHttpResponse intercept(HttpRequest request, byte[] body,
							ClientHttpRequestExecution execution) throws IOException {
						return execution.execute(new HttpRequestWrapper(request) {
							@Override
							public URI getURI() {
								return UriComponentsBuilder.fromUri(request.getURI())
										.buildAndExpand(Map.of("partition", MDC.get("partition"))).toUri();
							}
						}, body);
					}
				}).build();


		restClient.get().uri("/index.html").retrieve().body(String.class)

It's not working, and not elegant, please rethink the solution I proposed.

Exception in thread "main" java.lang.IllegalArgumentException: Not enough variable values available to expand 'partition'
	at org.springframework.web.util.UriComponents$VarArgsTemplateVariables.getValue(UriComponents.java:370)
	at org.springframework.web.util.UriComponents.expandUriComponent(UriComponents.java:263)
	at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:438)
	at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:53)
	at org.springframework.web.util.UriComponents.expand(UriComponents.java:172)
	at org.springframework.web.util.DefaultUriBuilderFactory$DefaultUriBuilder.build(DefaultUriBuilderFactory.java:459)
	at org.springframework.web.util.DefaultUriBuilderFactory.expand(DefaultUriBuilderFactory.java:204)
	at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.uri(DefaultRestClient.java:317)
	at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.uri(DefaultRestClient.java:288)

@bclozel
Copy link
Member

bclozel commented Jan 7, 2025

I don't think the uri template should leave a path variable in. The interceptor should change the host instead.

@quaff
Copy link
Contributor Author

quaff commented Jan 7, 2025

I don't think the uri template should leave a path variable in. The interceptor should change the host instead.

OK, it will works, but not elegant.

@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
	return execution.execute(new HttpRequestWrapper(request) {
		@Override
		public URI getURI() {
			return UriComponentsBuilder.fromUri(request.getURI()).host(MDC.get("partition") + "." + request.getURI().getHost()).build().toUri();
		}
	}, body);
}

@bclozel
Copy link
Member

bclozel commented Jan 7, 2025

I'll discuss #34189 with the team.

@quaff
Copy link
Contributor Author

quaff commented Jan 15, 2025

I'll discuss #34189 with the team.

Sorry to disturb, any updates?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: declined A suggestion or change that we don't feel we should currently apply
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support dynamic default uri variables for RestClient
3 participants