Creating A Simple Route

1
2
3
4
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
  return builder.routes().build();
}
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();
}
1
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"
}

Using Spring Cloud CircuitBreaker

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();
}
1
curl --dump-header - --header 'Host: www.circuitbreaker.com'

Use --dump-header to see the response header. The - after --dump-header tells cURL to print the headers to stdout.

1
2
HTTP/1.1 504 Gateway Timeout
content-length: 0
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();
}
1
2
3
4
@RequestMapping("/fallback")
public Mono<String> fallback() {
  return Mono.just("fallback");
}
1
curl --dump-header - --header 'Host: www.circuitbreaker.com'
1
2
3
4
HTTP/1.1 200 OK
transfer-encoding: chunked
Content-Type: text/plain;charset=UTF-8
fallback

Writing Tests

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;
  }
}
1
@EnableConfigurationProperties(UriConfiguration.class)
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();
}
1
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()));
  }
}

Reference