Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Vector;
import java.util.function.Function;
Expand All @@ -50,6 +52,7 @@
import org.springframework.core.Conventions;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.RequestEntity;
import org.springframework.http.RequestEntity.BodyBuilder;
Expand Down Expand Up @@ -147,6 +150,10 @@ public class ProxyExchange<T> {

private URI uri;

private String uriTemplate;

private Map<String, ?> uriVariables;

private RestTemplate rest;

private Object body;
Expand Down Expand Up @@ -247,6 +254,8 @@ public ProxyExchange<T> excluded(String... names) {
*/
public ProxyExchange<T> uri(URI uri) {
this.uri = uri;
this.uriTemplate = null;
this.uriVariables = null;
return this;
}

Expand All @@ -264,6 +273,21 @@ public ProxyExchange<T> uri(String uri) {
}
}

/**
* Sets the uri for the backend call using a URI template with variables. When a
* template is provided, the downstream {@link RestTemplate} call preserves the
* template pattern for observability (e.g. Micrometer URI tags).
* @param uriTemplate the URI template (e.g. {@code "http://service/foos/{id}"})
* @param uriVariables the variables to expand in the template
* @return this for convenience
*/
public ProxyExchange<T> uri(String uriTemplate, Map<String, ?> uriVariables) {
this.uriTemplate = uriTemplate;
this.uriVariables = uriVariables;
this.uri = rest.getUriTemplateHandler().expand(uriTemplate, uriVariables);
return this;
}

public String path() {
return (String) this.webRequest.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE,
WebRequest.SCOPE_REQUEST);
Expand Down Expand Up @@ -357,6 +381,11 @@ private ResponseEntity<T> exchange(RequestEntity<?> requestEntity) {
if (type instanceof TypeVariable || type instanceof WildcardType) {
type = Object.class;
}
if (this.uriTemplate != null && this.uriVariables != null) {
return rest.exchange(this.uriTemplate, Objects.requireNonNull(requestEntity.getMethod()),
new HttpEntity<>(requestEntity.getBody(), requestEntity.getHeaders()),
ParameterizedTypeReference.forType(type), this.uriVariables);
}
return rest.exchange(requestEntity, ParameterizedTypeReference.forType(type));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import io.micrometer.core.instrument.MeterRegistry;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -72,6 +73,9 @@ public class ProductionConfigurationTests {
@Autowired
private TestApplication application;

@Autowired
private MeterRegistry meterRegistry;

@LocalServerPort
private int port;

Expand All @@ -86,6 +90,17 @@ public void get() {
assertThat(rest.getForObject("/proxy/0", Foo.class).getName()).isEqualTo("bye");
}

@Test
public void getWithUriTemplate() {
assertThat(rest.getForObject("/proxy/template/0", Foo.class).getName()).isEqualTo("bye");
}

@Test
public void getWithUriTemplatePreservesUriTagForObservability() {
rest.getForObject("/proxy/template/0", Foo.class);
assertThat(meterRegistry.find("http.client.requests").tag("uri", "/foos/{id}").timer()).isNotNull();
}

@Test
public void path() {
assertThat(rest.getForObject("/proxy/path/1", Foo.class).getName()).isEqualTo("foo");
Expand Down Expand Up @@ -363,6 +378,11 @@ public ResponseEntity<?> proxyFoos(@PathVariable Integer id, ProxyExchange<?> pr
return proxy.uri(home.toString() + "/foos/" + id).get();
}

@GetMapping("/proxy/template/{id}")
public ResponseEntity<?> proxyWithTemplate(@PathVariable Integer id, ProxyExchange<?> proxy) {
return proxy.uri(home.toString() + "/foos/{id}", Map.of("id", String.valueOf(id))).get();
}

@GetMapping("/proxy/path/**")
public ResponseEntity<?> proxyPath(ProxyExchange<?> proxy, UriComponentsBuilder uri) {
String path = proxy.path("/proxy/path/");
Expand Down