12.4.1 获取资源

作为使用 WebClient 的样例,假设您需要通过 Taco Cloud API 根据 ID 获取 Ingredient 对象。如果使用 RestTemplate,那么您可能会使用 getForObject() 方法。但是,借助 WebClient 的话,您可以构建请求、获取响应并抽取一个会发布 Ingredient 对象的 Mono:

Mono<Ingredient> ingredient = WebClient.create()
  .get()
  .uri("http://localhost:8080/ingredients/{id}", ingredientId)
  .retrieve()
  .bodyToMono(Ingredient.class);

ingredient.subscribe(i -> { ... });

​ 在这里,您使用 create() 创建了一个新的 WebClient 实例。然后,您可以使用 get()uri() 定义对 `http://localhost:8080/ingredients/{id} 的 GET 请求,其中 {id} 占位符将会被 ingredientId 的值所替换。接着,retrieve() 会执行请求。最后,我们调用 bodyToMono()将响应体的载荷抽取到 Mono<Ingredient> 中,就可以继续使用 Mono 的额外操作了。

​为了对 bodyToMono() 返回 Mono 进行额外的操作,需要注意的很重要的一点是要在请求发送之前对其进行订阅。发送请求获取值的集合是非常容易的。例如,如下的代码片段将获取所有 Ingredient:

Flux<Ingredient> ingredients = WebClient.create()
  .get()
  .uri("http://localhost:8080/ingredients")
  .retrieve()
  .bodyToFlux(Ingredient.class);
ingredients.subscribe(i -> { ... });

​ 大部分而言,获取多个条目与获取单个条目是相同的。最大的差异在于我们不再是使用 bodyToMono() 将响应体抽取为 Mono,而是使用 bodyToFlux() 将其抽取为一个 Flux。与 bodyToMono() 类似,bodyToFlux() 返回的 Flux 还没有被订阅。在数据流过之前,您可以对 Flux 添加一些额外的操作(过滤、映射等)。因此,非常重要的一点就是要订阅结果所形成的 Flux,否则请求将始终不会发送。

使用基础 URI 发送请求

​您可能会发现在很多请求中都会使用一个通用的基础 URI。这样的话,创建 WebClient bean 的时候设置一个基础 URI 并将其注入到所需的地方是非常有用的。这样的 bean 可以按照如下的方式来声明(任何使用了 @Configuration 注解的类):

@Bean
public WebClient webClient() {
  return WebClient.create("http://localhost:8080");
}

​然后,在想要使用基础 URI 的任意地方,您都可以将 WebClient bean 注入进来并按照如下的方式来使用:

@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 -> { ... });
}

​因为 WebClient 已经创建好了,所以您可以通过 get() 方法直接使用它。对于 URI 来说,您只需要调用 uri() 指定相对于基础 URI 的相对路径即可。

对长时间运行的请求进行超时处理

​您需要考虑的一件事情就是,网络并不是始终可靠的,或者并不像您预期的那么快,远程服务器在处理请求时有可能会非常缓慢。理想情况下,对远程服务的请求会在一个合理的时间内返回。无法正常返回的话,客户端要是能够避免陷入长时间等待响应的窘境就好了。为了避免客户端请求被缓慢的网络或服务阻塞,您可以使用 Flux 或 Mono 的 timeout() 方法,为等待数据发布的过程设置一个时长限制。作为样例,您可以考虑一下如何为获取配料数据使用 timeout() 方法:

Flux<Ingredient> ingredients = webclient
  .get()
  .uri("/ingredients")
  .retrieve()
  .bodyToFlux(Ingredient.class);

ingredients
  .timeout(Duration.ofSeconds(1))
  .subscribe(
    i -> { ... },
    e -> {
    //handle timeout error
  });

​可以看到,在订阅 Flux 之前,调用了 timeout() 方法,将持续时间设置成了 1 秒。如果请求能够在 1 秒之内返回,就不会有任何问题。但是,如果请求花费的时间超过 1s,则会超时,然后会调用 subscribe() 的第二个参数进行错误处理。

results matching ""

    No results matching ""