這段介紹如何在reactive的Spring中發送request。
在SpringMVC中我們會使用RestTemplate,而在WebFlux中我們會使用WebClient來做到一樣的事情:
Mono<Ingredient> ingredient = WebClient.create()
.get()
.uri("http://localhost:8081/ingredients/{id}", ingredientId)
.retrieve()
.bodyToMono(Ingredient.class);
ingredient.subscribe(i -> { ... });
若想要加base URI到webclient中,可以建置一個Bean來做到:
@Bean
public WebClient webClient() {
return WebClient.create("http://localhost:8081");
}
@Autowired
WebClient webClient;
public Mono<Ingredient> getIngredientById(String ingredientId) {
Mono<Ingredient> ingredient = webClient
.get()
.uri("/ingredients/{id}", ingredientId)
.retrieve()
.bodyToMono(Ingredient.class);
ingredient.subscribe(i -> { ... });
}
設置timeout可串接在stream中:
Flux<Ingredient> ingredients = webclient
.get()
.uri("/ingredients")
.retrieve()
.bodyToFlux(Ingredient.class);
ingredients
.timeout(Duration.ofSeconds(1))
.subscribe(
i -> { ... },
e -> {
// handle timeout error
});
夾帶資料發送post:
Mono<Ingredient> ingredientMono = Mono.just(
new Ingredient("Valyrian", "Valyrian steel", Ingredient.Type.BLADE));
Mono<Ingredient> result = webClient
.post()
.uri("/ingredients")
.body(ingredientMono, Ingredient.class)
.retrieve()
.bodyToMono(Ingredient.class);
result.subscribe(i -> { ... });
可使用bodyValue直接把object放進request中:
Ingredient ingredient = ...;
Mono<Ingredient> result = webClient
.post()
.uri("/ingredients")
.bodyValue(ingredient)
.retrieve()
.bodyToMono(Ingredient.class);
result.subscribe(i -> { ... });
處理不同的http status,可在接回來的Mono或Flux接上subscribe時處理:
ingredientMono.subscribe(
ingredient -> {
// handle the ingredient data
...
},
error-> {
// deal with the error
...
});
或者在webclient的stream中使用onStatus處理:
Mono<Ingredient> ingredientMono = webClient
.get()
.uri("http://localhost:8081/ingredients/{id}", ingredientId)
.retrieve()
.onStatus(HttpStatus::is4xxClientError,
response -> Mono.just(new UnknownIngredientException()))
.bodyToMono(Ingredient.class);
以上範例都是透過retrieve()來處理response,背地裡是將ResponseSpec這個物件作處理後拿出其內容,不過如果我們需要header之類的response資訊時,就不適合直接使用retrieve()了,要用exchangeToMono或exchangeToFlux來拿到完整的ResponseSpec物件:
Mono<Ingredient> ingredientMono = webClient
.get()
.uri("http://localhost:8081/ingredients/{id}", ingredientId)
.exchangeToMono(cr -> {
if (cr.headers().header("X_UNAVAILABLE").contains("true")) {
return Mono.empty();
}
return Mono.just(cr);
})
.flatMap(cr -> cr.bodyToMono(Ingredient.class));