在 rest API 逻辑中使用反应时如何避免 Mono#block()

How to avoid Mono#block() when using reactive in rest API logic

所以问题如下:

  1. 我公开 API 以在我的服务中注册用户
  2. 在我要注册用户之前,我想在外部服务中验证他(休息API)
  3. 我想以正确的响应状态通知用户他是否已创建(201 或某些错误代码)

我创建用户的代码如下所示:

  @Transactional
  public void createUser(UserDto userDto) {
    UserEntity newUser = userFactory.createUser(userDto);
    String userId = userDto.getUserId();

    externalApiService
        .validateUser(userId)
        .subscribe(a-> userRepository.save(newUser));
  }

用户验证如下:

  public Mono<Void> validateUser(String userId) {
    return webClient
        .get(...)
        .retrieve()
        .toEntity(Void.class);
    //Exact request is bit more complicated, but those details shouldn't be crucial in this example
  }

我正在使用它的端点看起来像这样:

  @PostMapping
  public ResponseEntity<Void> createUser(@Valid @RequestBody UserDto dto) {
    userService.createUser(dto);
    return new ResponseEntity<>(HttpStatus.CREATED);
  }

当我使用阻塞 RestTemplate 或对刚从 WebClient 获得的 Mono 使用 block() 时,这非常容易。如果对外部服务的 REST 调用出现异常,我的全局异常处理程序将完成这项工作并使用正确的响应代码响应用户。

但是,如果我想充分利用响应式编程的强大功能而不想 block() 怎么办?我不知道如何正确地做到这一点。谁能给我一些建议?

如果我的问题很简单,请原谅,我对 WebClient 和反应式编程完全陌生。

WebClient 是一个反应式客户端,将它与 non-reactive Spring MVC 一起使用的唯一方法是 block.

要使流程充分 non-blocking,所有 components/functions 都应该是反应式的。您需要切换到 Spring WebFlux 并使 createUser 反应。

@PostMapping
public Mono<ResponseEntity<Void>> createUser(@RequestBody UserDto dto) {
    return userService.createUser(dto)
            .map(user -> ResponseEntity.status(HttpStatus.CREATED).build());
}

此代码假定 createUser 是反应性的

public static Mono<UserEntity> createUser(UserDto dto) {
    ...
}

如果 userRepository 不是反应式的,您可以将 non-reactive 代码和 运行 包装在单独的调度程序 How Do I Wrap a Synchronous, Blocking Call?.