反应式嵌套flatMap调用出现问题

时间:2019-08-22 18:11:18

标签: spring-webflux project-reactor

我需要对两个Monos执行操作。 困难在于,一个取决于另一个的结果。

让我解释一下:

  • 我有一个Mono<User>(我是从ServerRequest得到的; User是一个POJO)。

  • 我需要能够从上述Mono中提取用户电子邮件,并将其传递给UserRepository,以便检查该电子邮件是否已存在于数据库中。

  • 如果用户已经存在,我将抛出一个400错误;否则,我将保存ServerRequest中包含的用户。

这是我在handler中尝试过的内容:

public Mono<ServerResponse> saveUser(ServerRequest serverRequest) {
    return serverRequest.bodyToMono(User.class)
        .flatMap(user -> userRepository
            .findByEmail(user.getEmail())
            .flatMap(foundUser -> {
                if (foundUser != null) {
                    System.out.println("found:" + foundUser);
                    throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Email already exists");
                } else {
                    System.out.println("creating" + user);
                    return status(CREATED).contentType(APPLICATION_JSON).body(userRepository.save(user), User.class);
                }
            }));
}

User

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    @Id
    private Integer id;
    @Size(min = 2)
    private String firstName;
    @Size(min = 2)
    private String lastName;
    @Email
    private String email;
}

UserRepository

public interface UserRepository extends ReactiveCrudRepository<User, Long> {

    @Query("select id, first_name, last_name, email from user u where u.email = :email")
    Mono<User> findByEmail(String email);
}

但是,看来我的订阅存在问题:调用端点时,我的System.out.println都没有被调用。有人可以帮忙吗?

编辑1 :这是router,调用上述handler方法:

@Configuration
public class UserRouter {

    @Bean
    public RouterFunction<ServerResponse> route(UserHandler userHandler) {
        return RouterFunctions.route()
            .GET("/api/user", accept(APPLICATION_JSON), userHandler::getUsers)
            .POST("/api/sign-up", accept(APPLICATION_JSON), userHandler::saveUser)
            .build();
    }
}

1 个答案:

答案 0 :(得分:2)

这里的问题是,当找不到用户时,您期望为null,但是,在反应式流中null为无效。相反,反应式流具有专用的空状态和专用的运算符来处理空情况。

在您的示例中,您可以执行以下操作:

public Mono<ServerResponse> saveUser(ServerRequest serverRequest)
{
    return serverRequest.bodyToMono(User.class)
                        .flatMap(this::createUserIfNotExists);
}

private Mono<ServerResponse> createUserIfNotExists(User user)
{
    return userRepository.findByEmail(user.getEmail())
                         .hasElement()
                         .flatMap(foundUser ->
                         {
                             if (foundUser)
                             {
                                 System.out.println("found user");
                                 throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
                                         "Email already exists");
                             } else
                             {
                                 System.out.println("creating user");
                                 return status(CREATED).contentType(APPLICATION_JSON)
                                                       .body(userRepository.save(user), User.class);
                             }
                         });
}

或另一种选择:

public Mono<ServerResponse> saveUser(ServerRequest serverRequest)
{
    return serverRequest.bodyToMono(User.class)
                        .flatMap(this::createUserIfNotExists);
}

private Mono<ServerResponse> createUserIfNotExists(User user)
{
    return userRepository.findByEmail(user.getEmail())
                         .flatMap(foundUser ->
                                 status(BAD_REQUEST)
                                         .contentType(APPLICATION_JSON)
                                         .body(BodyInserters.fromObject("User already exists."))
                         )
                         .switchIfEmpty(
                                 userRepository.save(user)
                                               .flatMap(newUser -> status(CREATED).contentType(APPLICATION_JSON)
                                                                                  .body(BodyInserters.fromObject(newUser)))
                         );
}