前回の記事でWebFluxの基本的なAPI呼び出しの順次・並列の実装例を紹介しました。
今回はnullableなMono/Fluxの後続処理として、nullの場合/nullじゃない場合の分岐の実装例を紹介します。
まずは分岐を考えない場合の実装
順次処理(nullの考慮なし)
@GetMapping("/headers/find-by-user/{userId}")
public Mono<HeadersByUser> findHeadersByUserId(@NotNull @PathVariable final String userId) {
return userService.read(userId) // 1
.flatMap(user -> headerService.findByUserId(user.getUserId()) // 2
.collectList()
.map(headers -> HeadersByUser.builder() // 3
.user(user)
.headers(headers)
.build()));
}
- IDからユーザを取得
- 紐付く記事ヘッダ一覧を取得
- 最後にDtoに詰めて返す
上の実装は 1.ユーザを取得できない場合(=null)
の考慮が漏れています。
そこでリファクタしたいのですが、以下のように実装するのは誤りです。
誤ったnullの後続処理
@GetMapping("/headers/find-by-user/{userId}")
public Mono<HeadersByUser> findHeadersByUserId(@NotNull @PathVariable final String userId) {
return userService.read(userId) // 1
.flatMap(user -> {
if (user != null) {
// UserがあるならDtoに詰めて返す
return headerService.findByUserId(user.getUserId()) // 2
.collectList()
.map(headers -> HeadersByUser.builder() // 3
.user(user)
.headers(headers)
.build());
}
else {
// Userが無ければ空のDtoを返す(※誤り)
return Mono.just(HeadersByUser.builder().build()); // 2'
}
});
}
一見すると問題なさそうに見えますが、
ユーザが取得できない場合 .flatMap(user -> ...)
には処理が入らずスキップされます。
そのためelse { ... }
はデッドコードとなり、空のDtoを返すことはできません。
nullの後続はswitchIfEmptyで処理する
ユーザが取得できなかった場合(Mono<empty>)の後続処理はswitchIfEmpty
に書きます。
正しいnull(empty)の後続処理
@GetMapping("/headers/find-by-user/{userId}")
public Mono<HeadersByUser> findHeadersByUserId(@NotNull @PathVariable final String userId) {
return userService.read(userId) // 1
.flatMap(user -> headerService.findByUserId(user.getUserId()) // 2
.collectList()
.map(headers -> HeadersByUser.builder() // 3
.user(user)
.headers(headers)
.build())
)
.switchIfEmpty(
Mono.just(HeadersByUser.builder().build()) // 2'
);
}
これでユーザが取得できた場合/できなかった場合とで、後続の処理を分岐することができます。
今回のまとめ
先行のMono/Fluxがnull(empty)の場合
- 後続の
map
やflatMap
には処理が入らない - 何か処理したい場合は
switchIfEmpty
に書く
となります。
知っていればなんてことはありませんね。