Bean of type RouteLocator in Application.java.1
2
3
4
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes().build();
}
RouteLocatorBuilder lets you add predicates and filters to your routes so that you can route handle based on certain conditionshttps://httpbin.org/get when request is made to the Gateway at /get.Hello request header with a value of World to the request before it is routed:1
2
3
4
5
6
7
8
9
10
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route( p -> p
.path("/get")
.filter( f -> f
.addRequesetHeader("Hello", "World"))
.url("http://httpbin.org:80")
).build();
}
http://localhost:8080/get1
curl http://localhost:8080/get
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"args": {},
"headers": {
"Accept": "*/*",
"Connection": "close",
"Forwarded": "proto=http;host=\"localhost:8080\";for=\"0:0:0:0:0:0:0:1:56207\"",
"Hello": "World", // added request header
"Host": "httpbin.org",
"User-Agent": "curl/7.54.0",
"X-Forwarded-Host": "localhost:8080"
},
"origin": "0:0:0:0:0:0:0:1, 73.68.251.70",
"url": "http://localhost:8080/get"
}
1
implementation("org.springframework.cloud:spring-cloud-starter-circuitbreaker-reactor-resilience4j")
1
2
3
4
5
6
7
8
9
10
11
12
13
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route(p -> p
.path("/get")
.filters(f -> f.addRequestHeader("Hello", "World"))
.uri("http://httpbin.org:80")
).route(p -> p
.host("*.circuitbreaker.com")
.filters(f -> f.circuitBreaker(config -> config.setName("mycmd")))
.uri("http://httpbin.org:80")
).build();
}
circuitbreaker.com, we route the request to HTTPBin and wrap that request in a circuit breaker./delay/3.Host header that has a host of circuitbreaker.com.1
curl --dump-header - --header 'Host: www.circuitbreaker.com'
Use
--dump-headerto see the response header. The-after--dump-headertells cURL to print the headers to stdout.
1
2
HTTP/1.1 504 Gateway Timeout
content-length: 0
504.fallback instead.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route(p -> p
.path("/get")
.filters(f -> f.addRequestHeader("Hello", "World"))
.uri("http://httpbin.org:80")
).route(p -> p
.host("*.circuitbreaker.com")
.filters(f -> f
.circuitBreaker(config -> config
.setName("mycmd")
.setFallbackUri("forward:/fallback")
)
).uri("http://httpbin.org:80")
).build();
}
/fallback in the Gateway application./fallback endpoint to our application.1
2
3
4
@RequestMapping("/fallback")
public Mono<String> fallback() {
return Mono.just("fallback");
}
1
curl --dump-header - --header 'Host: www.circuitbreaker.com'
200 back from the Gateway with the response body of fallback.1
2
3
4
HTTP/1.1 200 OK
transfer-encoding: chunked
Content-Type: text/plain;charset=UTF-8
fallback
UriConfiguration:1
2
3
4
5
6
7
8
9
10
11
12
@ConfigurationProperties
class UriConfiguration {
private String httpbin = "http://httpbin.org:80";
public String getHttpbin() {
return httpbin;
}
public void setHttpbin(String httpbin) {
this.httpbin = httpbin;
}
}
ConfigurationProperties, we need to also add a class-level annotation to Application.java.1
@EnableConfigurationProperties(UriConfiguration.class)
myRoutes method:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder, UriConfiguration uriConfiguration) {
String httpUri = uriConfiguration.getHttpbin();
return builder.routes()
.route(p -> p
.path("/get")
.filters(f -> f.addRequestHeader("Hello", "World"))
.uri(httpUri))
.route(p -> p
.host("*.circuitbreaker.com")
.filters(f -> f
.circuitBreaker(config -> config
.setName("mycmd")
.setFallbackUri("forward:/fallback")))
.uri(httpUri))
.build();
}
ApplicationTest in src/main/test/java/gateway1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {"httpbin=http://localhost:${wiremock.server.port}"})
@AutoConfigureWireMock(port = 0)
public class ApplicationTest {
@Autowired
private WebTestClient webClient;
@Test
public void contextLoads() throws Exception {
//Stubs
stubFor(get(urlEqualTo("/get"))
.willReturn(aResponse()
.withBody("{\"headers\":{\"Hello\":\"World\"}}")
.withHeader("Content-Type", "application/json")));
stubFor(get(urlEqualTo("/delay/3"))
.willReturn(aResponse()
.withBody("no fallback")
.withFixedDelay(3000)));
webClient
.get().uri("/get")
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.headers.Hello").isEqualTo("World");
webClient
.get().uri("/delay/3")
.header("Host", "www.circuitbreaker.com")
.exchange()
.expectStatus().isOk()
.expectBody()
.consumeWith(
response -> assertThat(response.getResponseBody()).isEqualTo("fallback".getBytes()));
}
}
@AutoConfigureWireMock(port = 0) annotation starts WireMock on a random port for us.WebTestClient to make requests to the Gateway and validate the responses.