Моно switchIfEmpty () всегда вызывается - PullRequest
0 голосов
/ 26 января 2019

У меня есть два метода.
Основной метод:

@PostMapping("/login")
public Mono<ResponseEntity<ApiResponseLogin>> loginUser(@RequestBody final LoginUser loginUser) {
    return socialService.verifyAccount(loginUser)
            .flatMap(socialAccountIsValid -> {
                if (socialAccountIsValid) {
                    return this.userService.getUserByEmail(loginUser.getEmail())
                            .switchIfEmpty(insertUser(loginUser))
                            .flatMap(foundUser -> updateUser(loginUser, foundUser))
                            .map(savedUser -> {
                                String jwts = jwt.createJwts(savedUser.get_id(), savedUser.getFirstName(), "user");
                                return new ResponseEntity<>(HttpStatus.OK);
                            });
                } else {
                    return Mono.just(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
                }
            });

}

И этот вызванный метод (служба вызывает внешний API):

public Mono<User> getUserByEmail(String email) {
    UriComponentsBuilder builder = UriComponentsBuilder
            .fromHttpUrl(USER_API_BASE_URI)
            .queryParam("email", email);
    return this.webClient.get()
            .uri(builder.toUriString())
            .exchange()
            .flatMap(resp -> {
                if (Integer.valueOf(404).equals(resp.statusCode().value())) {
                    return Mono.empty();
                } else {
                    return resp.bodyToMono(User.class);
                }
            });
} 

В приведенном выше примереswitchIfEmpty() всегда вызывается из основного метода, даже когда возвращается результат с Mono.empty().

Я не могу найти решение для этой простой проблемы.
Следующее также не работает:

Mono.just(null) 

Поскольку метод выдаст исключение nullpointerexception.

Я также не могу использовать метод flatMap для проверки того, что foundUser имеет значение null.
К сожалению, flatMap вообще не вызывается в случае, если я возвращаю Mono.empty(), поэтому я не могудобавить условие здесь тоже.

Любая помощь приветствуется.

@ SimY4

   @PostMapping("/login")
    public Mono<ResponseEntity<ApiResponseLogin>> loginUser(@RequestBody final LoginUser loginUser) {
        userExists = false;
        return socialService.verifyAccount(loginUser)
                .flatMap(socialAccountIsValid -> {
                    if (socialAccountIsValid) {
                        return this.userService.getUserByEmail(loginUser.getEmail())
                                .flatMap(foundUser -> {
                                    return updateUser(loginUser, foundUser);
                                })
                                .switchIfEmpty(Mono.defer(() -> insertUser(loginUser)))
                                .map(savedUser -> {
                                    String jwts = jwt.createJwts(savedUser.get_id(), savedUser.getFirstName(), "user");
                                    return new ResponseEntity<>(HttpStatus.OK);
                                });
                    } else {
                        return Mono.just(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
                    }
                });

    }

1 Ответ

0 голосов
/ 29 января 2019

Это потому, что switchIfEmpty принимает Mono «по значению». Это означает, что еще до того, как вы подпишетесь на свой моно, оценка этого альтернативного моно уже запущена.

Представьте себе метод, подобный этому:

Mono<String> asyncAlternative() {
    return Mono.fromFuture(CompletableFuture.supplyAsync(() -> {
        System.out.println("Hi there");
        return "Alternative";
    }));
}

Если вы определите свой код следующим образом:

Mono<String> result = Mono.just("Some payload").switchIfEmpty(asyncAlternative());

Это всегда вызовет альтернативу, независимо от того, что происходит во время создания потока Для решения этой проблемы вы можете отложить оценку второго моно с помощью Mono.defer

Mono<String> result = Mono.just("Some payload")
        .switchIfEmpty(Mono.defer(() -> asyncAlternative()));

Таким образом, он будет печатать «Привет!» Только при запросе альтернативы

UPD:

Немного уточняю мой ответ. Проблема, с которой вы сталкиваетесь, связана не с Reactor, а с самим языком Java и с тем, как он разрешает параметры метода. Давайте рассмотрим код из первого предоставленного мною примера.

Mono<String> result = Mono.just("Some payload").switchIfEmpty(asyncAlternative());

Мы можем переписать это в:

Mono<String> firstMono = Mono.just("Some payload");
Mono<String> alternativeMono = asyncAlternative();
Mono<String> result = firstMono.switchIfEmpty(alternativeMono);

Эти два фрагмента кода семантически эквивалентны. Мы можем продолжить их развертывание, чтобы увидеть, в чем проблема:

Mono<String> firstMono = Mono.just("Some payload");
CompletableFuture<String> alternativePromise = CompletableFuture.supplyAsync(() -> {
        System.out.println("Hi there");
        return "Alternative";
    }); // future computation already tiggered
Mono<String> alternativeMono = Mono.fromFuture(alternativePromise);
Mono<String> result = firstMono.switchIfEmpty(alternativeMono);

Как вы можете видеть, будущие вычисления уже были запущены в тот момент, когда мы начали составлять наши Mono типы. Чтобы предотвратить нежелательные вычисления, мы можем заключить наше будущее в отложенную оценку:

Mono<String> result = Mono.just("Some payload")
        .switchIfEmpty(Mono.defer(() -> asyncAlternative()));

Который развернется в

Mono<String> firstMono = Mono.just("Some payload");
Mono<String> alternativeMono = Mono.defer(() -> Mono.fromFuture(CompletableFuture.supplyAsync(() -> {
        System.out.println("Hi there");
        return "Alternative";
    }))); // future computation defered
Mono<String> result = firstMono.switchIfEmpty(alternativeMono);

Во втором примере будущее находится в ловушке у ленивого поставщика и запланировано к исполнению только тогда, когда оно будет запрошено.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...