2つの方法があります。
主な方法:
_@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()
は常にmainメソッドから呼び出されます。
この単純な問題の解決策が見つかりません。
以下も機能しません:
_Mono.just(null)
_
メソッドがnullpointerexceptionをスローするためです。
また、使用できないのは、foundUser
がnullであることを確認するためのflatMapメソッドです。
残念ながら、Mono.empty()
を返す場合、flatMapはまったく呼び出されないため、ここにも条件を追加できません。
どんな助けでもありがたいです。
@ 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));
}
});
}
_
これは、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
を使用して2番目のモノの評価を延期できます。
Mono<String> result = Mono.just("Some payload")
.switchIfEmpty(Mono.defer(() -> asyncAlternative()));
このように、代替が要求されたときにのみ「こんにちは」を印刷します
PD:
私の答えについて少し詳しく説明します。あなたが直面している問題は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);
これら2つのコードスニペットは、意味的に同等です。それらをアンラップして、問題がどこにあるかを確認できます。
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);
2番目の例では、フューチャーはレイジーサプライヤーに閉じ込められ、要求される場合にのみ実行がスケジュールされます。