我正在尝试使用Webflux将生成的文件流式传输到另一个位置,但是,如果文件的生成遇到错误,则api返回成功,但DTO会在生成文件时详细说明错误,而不是文件本身。这使用的是非常古老且设计不佳的api,因此请原谅post和api设计的使用。
api调用(exchange())的响应是ClientResponse。从这里,我可以使用bodyToMono转换为ByteArrayResource并将其传输到文件,或者,如果在创建文件时出错,那么我也可以使用bodyToMono转换为DTO。但是,我似乎既不执行任何操作,也不依赖于ClientResponse标头的内容。
在运行时我收到由
引起的IllegalStateExceptionblock()/ blockFirst()/ blockLast()正在阻止,这在线程反应器-http-client-epoll-12中不支持
我认为我的问题是我无法在同一功能链中两次调用block()。
我的代码段如下:
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。
这可能吗?
答案 0 :(得分:6)
要在服务器请求池之外执行客户端请求,请使用 myWebClientMono.share().block();
答案 1 :(得分:5)
首先,几件事将帮助您了解解决此用例的代码段。
subscribe
也不是一个好主意。这或多或少就像在一个单独的线程中以一项任务开始该工作。完成后,您将获得一个回调(可以为subscribe
方法指定lambda),但实际上您正在将当前管道与该任务分离。在这种情况下,在您有机会读取完整的响应正文并将其写入文件之前,可以关闭客户端HTTP响应并清理资源。DataBuffer
(认为可以缓冲的ByteBuffer实例)。void
),则可以调用阻塞。以下是您可以用来执行此操作的代码段:
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
实例(反应性运算符出于性能原因正在预取值),并将以反应性方式写入字节。
答案 2 :(得分:2)
正如在投票最多的答案中所述,永远不应该阻止。就我而言,这是唯一的选择,因为我们在命令式代码中使用了反应式库。可以通过wrapping the mono in a processor进行屏蔽:
myMono.toProcessor().block()
答案 3 :(得分:-2)
对我来说,添加 web 依赖解决了这个问题。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
答案 4 :(得分:-5)
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();