WebFluxフレームワークを使用して、Spring Boot 2.0とKotlinでアプリを開発しています。
トランザクションを保存する前に、ユーザーIDが存在するかどうかを確認したいと思います。モノが空かどうかを検証するなどの簡単なことにこだわっています。
fun createTransaction(serverRequest: ServerRequest) : Mono<ServerResponse> {
val transaction = serverRequest.body(BodyExtractors.toMono(Transaction::class.Java))
transaction.flatMap {
val user = userRepository.findById(it.userId)
// If it's empty, return badRequest()
}
return transaction.flatMap { transactionRepository.save(it).then(created(URI.create("/transaction/" + it.id)).build()) }
}
私がやりたいことはできますか?
Flux
/Mono
が空かどうかを確認できる手法
演算子を使用する_.switchIfEmpty
_/_.defaultIfEmpty
_/_Mono.repeatWhenEmpty
_
上記の演算子を使用すると、要素を出力せずにStreamが完了した場合に対応できます。
まず、onNext
が呼び出されていない場合、_.map
_、_.flatMap
_、_.filter
_などの演算子はまったく呼び出されないことに注意してください。あなたの場合、次のコード
_transaction.flatMap {
val user = userRepository.findById(it.userId)
// If it's empty, return badRequest()
}
return transaction.flatMap { transactionRepository.save(it).then(created(URI.create("/transaction/" + it.id)).build()) }
_
transaction
が空の場合、まったく呼び出されません。
フローが空の場合にケースを処理する必要がある場合、次のような演算子を検討する必要があります。
_transaction
.flatMap(it -> {
val user = userRepository.findById(it.userId)
})
.swithIfEmpty(Flux.defer(() -> Flux.just(badRequest())));
_
また、メインtransaction
から2つのサブフローを作成したことに注意しました。実際、次のコードはまったく実行されません。
_transaction.flatMap {
val user = userRepository.findById(it.userId)
// If it's empty, return badRequest()
}
_
メソッドから返される最後の1つだけが実行されます。これは、演算子.subscribe(...)
を使用してサブスクライブしていないために発生します。
2番目のポイントは、同じリクエスト本文に一度以上サブスクライブすることはできません(WebClient
の応答の制限の種類)。したがって、次の方法でリクエスト本文を共有する必要があるため、完成した例は次のようになります。
_fun createTransaction(serverRequest: ServerRequest): Mono<ServerResponse> {
val transaction = serverRequest.body(BodyExtractors.toMono(Transaction::class.Java)).cache()
transaction
.flatMap { userRepository.findById(it.userId) }
.flatMap { transaction.flatMap { transactionRepository.save(it) } }
.flatMap { ServerResponse.created(URI.create("/transaction/" + it.id)).build() }
.switchIfEmpty(transaction.flatMap { ServerResponse.badRequest().syncBody("missed User for transaction " + it.id) })
}
_
または、トランザクションフローを共有せずにTuple
を使用するより単純なケース:
_fun createTransaction(serverRequest: ServerRequest): Mono<ServerResponse> {
val emptyUser = !User()
val transaction = serverRequest.body<Mono<Transaction>>(BodyExtractors.toMono(Transaction::class.Java))
transaction
.flatMap { t ->
userRepository.findById(t.userId)
.map { Tuples.of(t, it) }
.defaultIfEmpty(Tuples.of(t, emptyUser))
}
.flatMap {
if (it.t2 != emptyUser) {
transactionRepository.save(it.t1)
.flatMap { ServerResponse.created(URI.create("/transaction/" + it.id)).build() }
} else {
ServerResponse.badRequest().syncBody("missed User for transaction " + it.t1.id)
}
}
}
_
私はリアクティブ(Java)とこのフォーラムの初心者だと言ってみましょう。モノは空の場合、後で実行されるコードを表すため、モノが空の場合、このコードを実際にチェックインできないと思います。それは理にかなっていますか?
私はJavaで動作するように見えますが、100%ではなくこれが最良のアプローチです)で似たようなものを書いたところです。
public Mono<ServerResponse> queryStore(ServerRequest request) {
Optional<String> postalCode = request.queryParam("postalCode");
Mono<ServerResponse> badQuery = ServerResponse.badRequest().build();
Mono<ServerResponse> notFound = ServerResponse.notFound().build();
if (!postalCode.isPresent()) { return badQuery; }
Flux<Store> stores = this.repository
.getNearByStores(postalCode.get(), 5);
return ServerResponse.ok().contentType(APPLICATION_JSON)
.body(stores, Store.class)
.switchIfEmpty(notFound);
}