7
3

More than 3 years have passed since last update.

nullableなMono/Fluxの後続処理を分岐する(WebFlux基本編)

Last updated at Posted at 2020-01-31

前回の記事でWebFluxの基本的なAPI呼び出しの順次・並列の実装例を紹介しました。
今回はnullableなMono/Fluxの後続処理として、nullの場合/nullじゃない場合の分岐の実装例を紹介します。

まずは分岐を考えない場合の実装

前回の実装例です。
activity_1-1.png

順次処理(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()));
  }
  1. IDからユーザを取得
  2. 紐付く記事ヘッダ一覧を取得
  3. 最後に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)の場合

  • 後続のmapflatMapには処理が入らない
  • 何か処理したい場合はswitchIfEmpty に書く

となります。
知っていればなんてことはありませんね。

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3