Webfluxを使用して生成されたファイルを別の場所にストリーミングしようとしていますが、ファイルの生成でエラーが発生した場合、APIは成功を返しますが、ファイル自体ではなく、ファイルの生成中にDTOがエラーの詳細を示します。これは非常に古く、設計が不十分なAPIを使用しているため、ポストおよびAPI設計の使用を許しません。
API呼び出し(exchange())からの応答はClientResponseです。ここから、ファイルにストリーミングできるbodyToMonoを使用してByteArrayResourceに変換できます。または、ファイルの作成でエラーが発生した場合は、bodyToMonoを使用してDTOに変換することもできます。ただし、ClientResponseのヘッダーの内容によっては、どちらも実行できないようです。
実行時に、次の原因によりIllegalStateExceptionが発生します。
block()/ blockFirst()/ blockLast()はブロッキングですが、スレッドreactor-http-client-epoll-12ではサポートされていません
私の問題は、同じ関数チェーンでblock()を2回呼び出せないことだと思います。
私のコードスニペットはそうです:
webClient.post()
.uri(uriBuilder -> uriBuilder.path("/file/")
.queryParams(params).build())
.exchange()
.doOnSuccess(cr -> {
if (MediaType.APPLICATION_JSON_UTF8.equals(cr.headers().contentType().get())) {
NoPayloadResponseDto dto = cr.bodyToMono(NoPayloadResponseDto.class).block();
createErrorFile(dto);
}
else {
ByteArrayResource bAr = cr.bodyToMono(ByteArrayResource.class).block();
createSpreadsheet(bAr);
}
}
)
.block();
基本的に、ヘッダーで定義されているMediaTypeに基づいて、ClientResponseを別の方法で処理したいと思います。
これは可能ですか?
最初に、この使用例を解決するコードスニペットを理解するのに役立ついくつかのこと。
subscribe
を呼び出すのも良い考えではありません。それは多かれ少なかれ、別のスレッドでタスクとしてそのジョブを開始することに似ています。完了するとコールバックが発生します(subscribe
メソッドにはラムダを指定できます)が、実際には現在のパイプラインとそのタスクを分離しています。この場合、応答本文全体を読み取ってファイルに書き込む機会を得る前に、クライアントHTTP応答を閉じてリソースをクリーンアップできます。DataBuffer
を提供します(プールできるByteBufferインスタンスを考えてください)。void
を返す)場合は、blockを呼び出すことができます。これを行うために使用できるコードスニペットを次に示します。
Mono<Void> fileWritten = WebClient.create().post()
.uri(uriBuilder -> uriBuilder.path("/file/").build())
.exchange()
.flatMap(response -> {
if (MediaType.APPLICATION_JSON_UTF8.equals(response.headers().contentType().get())) {
Mono<NoPayloadResponseDto> dto = response.bodyToMono(NoPayloadResponseDto.class);
return createErrorFile(dto);
}
else {
Flux<DataBuffer> body = response.bodyToFlux(DataBuffer.class);
return createSpreadsheet(body);
}
});
// Once you get that Mono, you should give plug it into an existing
// reactive pipeline, or call block on it, depending on the situation
ご覧のとおり、私たちはどこもブロックしておらず、I/Oを処理するメソッドはMono<Void>
を返しています。これは、処理が完了したときにエラーが発生したことを通知するdone(error)
コールバックに対応しています。起こりました。
createErrorFile
メソッドが何をすべきかわからないので、本文のバイトをファイルに書き込むだけのcreateSpreadsheet
のサンプルを提供しました。データバッファはリサイクル/プールされる可能性があるため、完了したら解放する必要があることに注意してください。
private Mono<Void> createSpreadsheet(Flux<DataBuffer> body) {
try {
Path file = //...
WritableByteChannel channel = Files.newByteChannel(file, StandardOpenOption.WRITE);
return DataBufferUtils.write(body, channel).map(DataBufferUtils::release).then();
} catch (IOException exc) {
return Mono.error(exc);
}
}
この実装により、アプリケーションは特定の時間にメモリ内にいくつかのDataBuffer
インスタンスを保持し(リアクティブオペレーターはパフォーマンス上の理由で値をプリフェッチします)、バイトがリアクティブな方法で到着するとバイトを書き込みます。
私の場合、exchange
とblock
をretrieve
に置き換える必要がありました。
これによりエラーが発生しました:
Mono<Boolean> booleanMono = webClient.get()
.exchange().block().bodyToMono(Boolean.class);
上記を次の行で置き換えると、私の問題が解決しました:
Mono<Boolean> booleanMono = webClient.get()
.retrieve().bodyToMono(Boolean.class);
RestResultMessage message= createWebClient()
.get()
.uri(uri)
.exchange()
.map(clientResponse -> {
//delegation
ClientResponseWrapper wrapper = new
ClientResponseWrapper(clientResponse);
return Mono.just(wrapper);
})
.block() //wait until request is not done
.map(result -> {
//convert to any data
if (!result.statusCode().isError()){
//extract the result from request
return create(RestResultMessage.Result.success, result.bodyToMono(String.class).block());}
} else {
return create(RestResultMessage.Result.error, result.statusCode().name());
}
})
.block();