首页 > 解决方案 > 在 Controller 中使用 WebClient 进行 API 调用

问题描述

所以我有一个简单的@RestController端点,我想在其中使用 WebClient 进行 API 调用,然后获取从该调用中获得的数据,进行更多 API 调用,处理数据,并最终返回一些基于数据的数据在这一切...

然而,这整个反应性的事情让我很困惑。我知道如何以旧方式执行此操作RestTemplate,没问题......我会认为这正是block()等待客户端完成然后我可以使用该数据的时间,但显然这是不允许在“反应式”控制器内部!

我创建了一个自定义 POJO ( ExternalApiResponse),它与来自 API 的 JSON 数据响应相匹配,它似乎工作正常......以下代码没有错误,并且完美地响应来自 API 调用的 JSON 数据并显示它从外部 API 获取后在浏览器中...

ExternalApiResponse实际上只包含List另一个 POJO,因为来自 API 调用的结果的格式类似于{ [ ] }(有没有更好的方法来做到这一点?),这意味着一个数组包装在一个对象中......我不想只返回API 结果,我想遍历它们,甚至对最初返回的 API 结果中的每个元素进行额外的 API 调用(在其他地方),然后返回已处理的新对象!

@RestController
@RequestMapping
public class Controller {
  @RequestMapping("/{id}")
  public Mono<ExternalApiResponse> queryApi(@PathVariable String id) {
    Mono<ExternalApiResponse> response = webClientBuilder.build()
    .get()
    .uri(uri)
    .accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .bodyToMono(ExternalApiResponse.class)
    
    return response;

  }
}

我认为有一种更合适的方式来构建它,但我不确定。任何线索都非常感谢!

标签: javaspring-bootspring-webfluxspring-webclient

解决方案


@RequestMapping("/{id}")
public Mono<MyApiResponse> queryApi(@PathVariable String id) {
    return webClientBuilder
        .build()
        .get()
        .uri(uri)
        .accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .bodyToMono(ExternalApiResponse.class)
        .flatMapIterable(externalApiResponse -> externalApiResponse.getList()) // switch to flux
        .flatMap(item -> process(item)) // process each item individually
        .collectList() // collect processed items into a list to wrap them
        .map(processedItems -> new MyApiResponse(processedItems)); // wrap
}

private Mono<ProcessedItem> process(Item item) {
    // todo
}

private static class ExternalApiResponse {
    List<Item> list;
}

private static class MyApiResponse {
    List<ProcessedItem> processedItems;
}

推荐阅读