Skip to content

Commit edf3b45

Browse files
authored
Merge pull request #16405 from Michaelin007/webfluxmono
https://jira.baeldung.com/browse/BAEL-7789
2 parents bb0116b + 7f87e88 commit edf3b45

6 files changed

Lines changed: 318 additions & 0 deletions

File tree

spring-reactive-modules/spring-reactive-client-2/pom.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,19 @@
5858
<artifactId>spring-boot-devtools</artifactId>
5959
<scope>runtime</scope>
6060
</dependency>
61+
<dependency>
62+
<groupId>org.openjdk.jmh</groupId>
63+
<artifactId>jmh-core</artifactId>
64+
<version>${jmh.version}</version>
65+
<scope>test</scope>
66+
</dependency>
67+
<dependency>
68+
<groupId>org.openjdk.jmh</groupId>
69+
<artifactId>jmh-generator-annprocess</artifactId>
70+
<version>${jmh.version}</version>
71+
<scope>test</scope>
72+
</dependency>
73+
6174
<dependency>
6275
<groupId>org.springframework</groupId>
6376
<artifactId>spring-test</artifactId>
@@ -141,6 +154,7 @@
141154
<reactor-spring.version>1.0.1.RELEASE</reactor-spring.version>
142155
<jetty-reactive-httpclient.version>1.1.6</jetty-reactive-httpclient.version>
143156
<resilience4j.version>1.7.1</resilience4j.version>
157+
<jmh.version>1.37</jmh.version>
144158
</properties>
145159

146160
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.baeldung.webclientretrievevsexchange;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class RetrieveAndExchangeApp {
8+
9+
public static void main(String[] args) {
10+
SpringApplication.run(RetrieveAndExchangeApp.class, args);
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package com.baeldung.webclientretrievevsexchange;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
import org.springframework.http.HttpStatusCode;
6+
import org.springframework.http.MediaType;
7+
import org.springframework.http.ResponseEntity;
8+
import org.springframework.web.bind.annotation.GetMapping;
9+
import org.springframework.web.bind.annotation.PathVariable;
10+
import org.springframework.web.bind.annotation.RestController;
11+
import org.springframework.web.reactive.function.client.WebClient;
12+
import reactor.core.publisher.Flux;
13+
import reactor.core.publisher.Mono;
14+
15+
@RestController
16+
public class RetrieveAndExchangeController {
17+
18+
private static final Logger logger = LoggerFactory.getLogger(RetrieveAndExchangeController.class);
19+
20+
WebClient client = WebClient.create("https://jsonplaceholder.typicode.com/users");
21+
22+
@GetMapping("/user/{id}")
23+
Mono<User> retrieveOneUser(@PathVariable int id) {
24+
return client.get()
25+
.uri("/{id}", id)
26+
.retrieve()
27+
.bodyToMono(User.class)
28+
.onErrorResume(Mono::error);
29+
}
30+
31+
@GetMapping("/user-status/{id}")
32+
Mono<User> retrieveOneUserAndHandleErrorBasedOnStatus(@PathVariable int id) {
33+
return client.get()
34+
.uri("/{id}", id)
35+
.retrieve()
36+
.onStatus(HttpStatusCode::is4xxClientError, response -> Mono.error(new RuntimeException("Client Error: can't fetch user")))
37+
.onStatus(HttpStatusCode::is5xxServerError, response -> Mono.error(new RuntimeException("Server Error: can't fetch user")))
38+
.bodyToMono(User.class);
39+
}
40+
41+
@GetMapping("/user-id/{id}")
42+
Mono<ResponseEntity<User>> retrieveOneUserWithResponseEntity(@PathVariable int id) {
43+
return client.get()
44+
.uri("/{id}", id)
45+
.accept(MediaType.APPLICATION_JSON)
46+
.retrieve()
47+
.toEntity(User.class)
48+
.onErrorResume(Mono::error);
49+
}
50+
51+
@GetMapping("/users")
52+
Flux<User> retrieveAllUsers() {
53+
return client.get()
54+
.retrieve()
55+
.bodyToFlux(User.class)
56+
.onErrorResume(Flux::error);
57+
}
58+
59+
@GetMapping("/user/exchange-alter/{id}")
60+
Mono<User> retrieveOneUserWithExchangeAndManipulate(@PathVariable int id) {
61+
return client.get()
62+
.uri("/{id}", id)
63+
.exchangeToMono(res -> res.bodyToMono(User.class))
64+
.map(user -> {
65+
user.setName(user.getName()
66+
.toUpperCase());
67+
user.setId(user.getId() + 100);
68+
return user;
69+
});
70+
}
71+
72+
@GetMapping("/user/exchange-mono/{id}")
73+
Mono<User> retrieveUsersWithExchangeAndError(@PathVariable int id) {
74+
return client.get()
75+
.uri("/{id}", id)
76+
.exchangeToMono(res -> {
77+
if (res.statusCode()
78+
.is2xxSuccessful()) {
79+
return res.bodyToMono(User.class);
80+
} else if (res.statusCode()
81+
.is4xxClientError()) {
82+
return Mono.error(new RuntimeException("Client Error: can't fetch user"));
83+
} else if (res.statusCode()
84+
.is5xxServerError()) {
85+
return Mono.error(new RuntimeException("Server Error: can't fetch user"));
86+
} else {
87+
return res.createError();
88+
}
89+
});
90+
}
91+
92+
@GetMapping("/user/exchange-header/{id}")
93+
Mono<User> retrieveUsersWithExchangeAndHeader(@PathVariable int id) {
94+
return client.get()
95+
.uri("/{id}", id)
96+
.exchangeToMono(res -> {
97+
if (res.statusCode()
98+
.is2xxSuccessful()) {
99+
logger.info("Status code: " + res.headers()
100+
.asHttpHeaders());
101+
logger.info("Content-type" + res.headers()
102+
.contentType());
103+
return res.bodyToMono(User.class);
104+
} else if (res.statusCode()
105+
.is4xxClientError()) {
106+
return Mono.error(new RuntimeException("Client Error: can't fetch user"));
107+
} else if (res.statusCode()
108+
.is5xxServerError()) {
109+
return Mono.error(new RuntimeException("Server Error: can't fetch user"));
110+
} else {
111+
return res.createError();
112+
}
113+
});
114+
}
115+
116+
@GetMapping("/user-exchange")
117+
Flux<User> retrieveAllUserWithExchange(@PathVariable int id) {
118+
return client.get()
119+
.exchangeToFlux(res -> res.bodyToFlux(User.class))
120+
.onErrorResume(Flux::error);
121+
}
122+
123+
@GetMapping("/user-exchange-flux")
124+
Flux<User> retrieveUsersWithExchange() {
125+
return client.get()
126+
.exchangeToFlux(res -> {
127+
if (res.statusCode()
128+
.is2xxSuccessful()) {
129+
return res.bodyToFlux(User.class);
130+
} else {
131+
return Flux.error(new RuntimeException("Error while fetching users"));
132+
}
133+
});
134+
}
135+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.baeldung.webclientretrievevsexchange;
2+
3+
public class User {
4+
5+
private int id;
6+
private String name;
7+
8+
public User() {
9+
}
10+
11+
public int getId() {
12+
return id;
13+
}
14+
15+
public void setId(int id) {
16+
this.id = id;
17+
}
18+
19+
public String getName() {
20+
return name;
21+
}
22+
23+
public void setName(String name) {
24+
this.name = name;
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package com.baeldung.webclientretrievevsexchange;
2+
3+
import org.openjdk.jmh.annotations.BenchmarkMode;
4+
import org.openjdk.jmh.annotations.Measurement;
5+
import org.openjdk.jmh.annotations.Setup;
6+
import org.openjdk.jmh.annotations.State;
7+
import org.openjdk.jmh.annotations.Scope;
8+
import org.openjdk.jmh.annotations.Warmup;
9+
import org.openjdk.jmh.annotations.Mode;
10+
import org.openjdk.jmh.annotations.Benchmark;
11+
import org.springframework.web.reactive.function.client.WebClient;
12+
import reactor.core.publisher.Flux;
13+
import reactor.core.publisher.Mono;
14+
15+
import java.util.concurrent.TimeUnit;
16+
17+
@State(Scope.Benchmark)
18+
@BenchmarkMode(Mode.AverageTime)
19+
@Warmup(iterations = 3, time = 10, timeUnit = TimeUnit.MICROSECONDS)
20+
@Measurement(iterations = 3, time = 10, timeUnit = TimeUnit.MICROSECONDS)
21+
public class RetrieveAndExchangeBenchmark {
22+
private WebClient client;
23+
24+
@Setup
25+
public void setup() {
26+
this.client = WebClient.create("https://jsonplaceholder.typicode.com/users");
27+
}
28+
29+
@Benchmark
30+
public Mono<User> retrieveOneUserUsingRetrieveMethod() {
31+
return client.get()
32+
.uri("/1")
33+
.retrieve()
34+
.bodyToMono(User.class)
35+
.onErrorResume(Mono::error);
36+
37+
}
38+
39+
@Benchmark
40+
public Flux<User> retrieveManyUserUsingRetrieveMethod() {
41+
return client.get()
42+
.retrieve()
43+
.bodyToFlux(User.class)
44+
.onErrorResume(Flux::error);
45+
}
46+
47+
@Benchmark
48+
public Mono<User> retrieveOneUserUsingExchangeToMono() {
49+
return client.get()
50+
.uri("/1")
51+
.exchangeToMono(res -> res.bodyToMono(User.class))
52+
.onErrorResume(Mono::error);
53+
}
54+
55+
@Benchmark
56+
public Flux<User> retrieveManyUserUsingExchangeToFlux() {
57+
return client.get()
58+
.exchangeToFlux(res -> res.bodyToFlux(User.class))
59+
.onErrorResume(Flux::error);
60+
61+
}
62+
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.baeldung.webclientretrievevsexchange;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
6+
import org.springframework.test.web.reactive.server.WebTestClient;
7+
8+
@WebFluxTest
9+
class RetrieveAndExchangeIntegrationTest {
10+
11+
@Autowired
12+
private WebTestClient webTestClient;
13+
14+
@Test
15+
void givenFirstUser_whenRetrieveMethodIsUsed_thenReturnOk() throws Exception {
16+
this.webTestClient.get()
17+
.uri("/user/1")
18+
.exchange()
19+
.expectStatus()
20+
.isOk();
21+
}
22+
23+
@Test
24+
void givenFirstUser_whenRetreiveMethodIsUsedWithOnStatusHandler_thenReturnNotFound() throws Exception {
25+
this.webTestClient.get()
26+
.uri("/user-status/100")
27+
.exchange()
28+
.expectStatus()
29+
.is5xxServerError();
30+
}
31+
32+
@Test
33+
void givenFirstUser_whenExchangeMonoMethodIsUsed_thenReturnOk() throws Exception {
34+
this.webTestClient.get()
35+
.uri("/user/exchange-mono/1")
36+
.exchange()
37+
.expectStatus()
38+
.isOk();
39+
}
40+
41+
@Test
42+
void givenAllUsers_whenRetrieveMethodIsUsed_thenReturnOk() throws Exception {
43+
this.webTestClient.get()
44+
.uri("/users")
45+
.exchange()
46+
.expectStatus()
47+
.isOk();
48+
}
49+
50+
@Test
51+
void givenSingleUser_whenResponseBodyIsAltered_thenReturnOk() throws Exception {
52+
this.webTestClient.get()
53+
.uri("/user/exchange-alter/1")
54+
.exchange()
55+
.expectBody()
56+
.json("{\"id\":101,\"name\":\"LEANNE GRAHAM\"}");
57+
}
58+
59+
@Test
60+
void givenAllUsers_whenExchangeFluxMethodIsUsed_thenReturnOk() throws Exception {
61+
this.webTestClient.get()
62+
.uri("/user-exchange-flux")
63+
.exchange()
64+
.expectStatus()
65+
.isOk();
66+
}
67+
68+
}

0 commit comments

Comments
 (0)