Skip to content

Commit 24abe33

Browse files
authored
Merge pull request #4137 from fru1tworld/fix-3458-proxyexchange-uri-template
Add URI template support to ProxyExchange for observability
2 parents 6100cc4 + 6117ec4 commit 24abe33

File tree

2 files changed

+49
-0
lines changed

2 files changed

+49
-0
lines changed

spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/ProxyExchange.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
import java.util.LinkedHashSet;
3333
import java.util.List;
3434
import java.util.Locale;
35+
import java.util.Map;
36+
import java.util.Objects;
3537
import java.util.Set;
3638
import java.util.Vector;
3739
import java.util.function.Function;
@@ -50,6 +52,7 @@
5052
import org.springframework.core.Conventions;
5153
import org.springframework.core.MethodParameter;
5254
import org.springframework.core.ParameterizedTypeReference;
55+
import org.springframework.http.HttpEntity;
5356
import org.springframework.http.HttpHeaders;
5457
import org.springframework.http.RequestEntity;
5558
import org.springframework.http.RequestEntity.BodyBuilder;
@@ -147,6 +150,10 @@ public class ProxyExchange<T> {
147150

148151
private URI uri;
149152

153+
private String uriTemplate;
154+
155+
private Map<String, ?> uriVariables;
156+
150157
private RestTemplate rest;
151158

152159
private Object body;
@@ -247,6 +254,8 @@ public ProxyExchange<T> excluded(String... names) {
247254
*/
248255
public ProxyExchange<T> uri(URI uri) {
249256
this.uri = uri;
257+
this.uriTemplate = null;
258+
this.uriVariables = null;
250259
return this;
251260
}
252261

@@ -264,6 +273,21 @@ public ProxyExchange<T> uri(String uri) {
264273
}
265274
}
266275

276+
/**
277+
* Sets the uri for the backend call using a URI template with variables. When a
278+
* template is provided, the downstream {@link RestTemplate} call preserves the
279+
* template pattern for observability (e.g. Micrometer URI tags).
280+
* @param uriTemplate the URI template (e.g. {@code "http://service/foos/{id}"})
281+
* @param uriVariables the variables to expand in the template
282+
* @return this for convenience
283+
*/
284+
public ProxyExchange<T> uri(String uriTemplate, Map<String, ?> uriVariables) {
285+
this.uriTemplate = uriTemplate;
286+
this.uriVariables = uriVariables;
287+
this.uri = rest.getUriTemplateHandler().expand(uriTemplate, uriVariables);
288+
return this;
289+
}
290+
267291
public String path() {
268292
return (String) this.webRequest.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE,
269293
WebRequest.SCOPE_REQUEST);
@@ -357,6 +381,11 @@ private ResponseEntity<T> exchange(RequestEntity<?> requestEntity) {
357381
if (type instanceof TypeVariable || type instanceof WildcardType) {
358382
type = Object.class;
359383
}
384+
if (this.uriTemplate != null && this.uriVariables != null) {
385+
return rest.exchange(this.uriTemplate, Objects.requireNonNull(requestEntity.getMethod()),
386+
new HttpEntity<>(requestEntity.getBody(), requestEntity.getHeaders()),
387+
ParameterizedTypeReference.forType(type), this.uriVariables);
388+
}
360389
return rest.exchange(requestEntity, ParameterizedTypeReference.forType(type));
361390
}
362391

spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/ProductionConfigurationTests.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Map;
2424

2525
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
26+
import io.micrometer.core.instrument.MeterRegistry;
2627
import org.junit.jupiter.api.BeforeEach;
2728
import org.junit.jupiter.api.Disabled;
2829
import org.junit.jupiter.api.Test;
@@ -72,6 +73,9 @@ public class ProductionConfigurationTests {
7273
@Autowired
7374
private TestApplication application;
7475

76+
@Autowired
77+
private MeterRegistry meterRegistry;
78+
7579
@LocalServerPort
7680
private int port;
7781

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

93+
@Test
94+
public void getWithUriTemplate() {
95+
assertThat(rest.getForObject("/proxy/template/0", Foo.class).getName()).isEqualTo("bye");
96+
}
97+
98+
@Test
99+
public void getWithUriTemplatePreservesUriTagForObservability() {
100+
rest.getForObject("/proxy/template/0", Foo.class);
101+
assertThat(meterRegistry.find("http.client.requests").tag("uri", "/foos/{id}").timer()).isNotNull();
102+
}
103+
89104
@Test
90105
public void path() {
91106
assertThat(rest.getForObject("/proxy/path/1", Foo.class).getName()).isEqualTo("foo");
@@ -363,6 +378,11 @@ public ResponseEntity<?> proxyFoos(@PathVariable Integer id, ProxyExchange<?> pr
363378
return proxy.uri(home.toString() + "/foos/" + id).get();
364379
}
365380

381+
@GetMapping("/proxy/template/{id}")
382+
public ResponseEntity<?> proxyWithTemplate(@PathVariable Integer id, ProxyExchange<?> proxy) {
383+
return proxy.uri(home.toString() + "/foos/{id}", Map.of("id", String.valueOf(id))).get();
384+
}
385+
366386
@GetMapping("/proxy/path/**")
367387
public ResponseEntity<?> proxyPath(ProxyExchange<?> proxy, UriComponentsBuilder uri) {
368388
String path = proxy.path("/proxy/path/");

0 commit comments

Comments
 (0)