11.4.1 通过 GET 方式获取资源

作为使用 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 可以按照如下的方式来声明:

@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.create() 
  .get()   
  .uri("http://localhost:8080/ingredients")  
  .retrieve()   
  .bodyToFlux(Ingredient.class);  
ingredients.timeout(Duration.ofSeconds(1))  
  .subscribe(   
  	i -> { ... },  
  	e -> {    
      // handle timeout error   
    })

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

最后更新于