[英]Return relevant ServerResponse in case of Flux.error
我有一个 WebFlux 功能 REST 端点,但由于我的代码中抛出异常(例如无效路径变量上的 BadRequest),我无法返回自定义 http 错误。
考虑我的处理程序:
public Mono<ServerResponse> getStarships(ServerRequest request) {
String starshipType = request.pathVariable("type");
return ServerResponse
.ok()
.contentType(APPLICATION_JSON)
.body(starshipService.getFromSpacedock(starshipType), Starship.class)
.onErrorResume(InvalidStarshipTypeException.class,
e -> ServerResponse
.badRequest()
.bodyValue(e.getMessage()));
}
当starshipService.getFromSpacedock(starshipType)
返回Flux.just(new Starship())
时,一切都如预期的那样。
当它返回Flux.error(new InvalidStarshipTypeException("invalid starship type"))
时,我希望 onErrorResume 启动并返回我的自定义 BadRequest ServerResponse 和我的消息。
相反,我的端点以 500 响应(其中包含我的自定义异常)。 onErrorResume 被忽略。
我该如何解决这个问题?
我试过的:
ResponseStatusException
中:我得到了 400 但不是通过自定义 ServerResponse 路由。 这种方法的问题是我必须配置 Spring 以在以这种方式处理异常时显示消息,这是我不想要的。Flux<ServerResponse>
而不是Mono<ServerResponse>
:return starshipService.getFromSpacedock(starshipType) // remember, this is a Flux<Starship>
.flatMap(ships -> ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(ships, StarShip.class))
.onErrorResume(e -> ServerResponse.badRequest().bodyValue(e.getMessage()));
我有一个类似的问题。 要解决此问题,您需要首先将冷焊剂转换为热焊剂。 然后在热通量调用.next()
上,返回Mono<Starship>
。 在这个单声道上,调用.flatMap().switchIfEmpty().onErrorResume()
。 在flatMap()
中,将返回的 startship 对象与热通量连接起来。
这是修改后的代码片段以实现您想要的效果:
public Mono<ServerResponse> getStarships(ServerRequest request)
{
String starshipType = request.pathVariable("type");
Flux<Starship> coldStarshipFlux = starshipService.getFromSpacedock(starshipType);
//The following step is a very important step. It converts your cold flux into a hot flux.
Flux<Startship> hotStarshipFlux = coldStarshipFlux
.publish()
.refCount(1, Duration.ofSeconds(2));
return hotStarshipFlux.next()
.flatMap( starShipObj ->
{
Flux<Starship> flux = Mono.just(starShipObj)
.concatWith(hotStarshipFlux);
return ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(flux, Starship.class);
}
)
.switchIfEmpty(
ServerResponse.notFound().build()
)
.onErrorResume( t ->
{
if(t instanceof InvalidStarshipTypeException)
{
return ServerResponse.badRequest()
.contentType(MediaType.TEXT_PLAIN)
.bodyValue(t.getMessage());
}
else
{
return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
.contentType(MediaType.TEXT_PLAIN)
.bodyValue(t.getMessage());
}
});
}
代码.publish().refCount(1, Duration.ofSeconds(2));
是什么让你的冷焊剂变成热焊剂。 这样做很重要。
当使用热通量时,每个新订阅共享通量流中发出的元素,这些元素在订阅开始后发出。 因此,对.next()
的初始调用导致热通量发射第一个元素。 现在,当在同一个热通量上再次调用.concatWith()
方法时,热通量不会再次重新发射第一个元素,但会继续发射其流中的后续元素。 因此,流中所有剩余的元素,从第二个开始将被连接起来。
如果您没有将助焊剂转换为热助焊剂,而是在冷助焊剂上运行上述代码,那么对.next()
和.concatWith()
的调用都会导致冷助焊剂重新生成相同的数据两次,一次用于.next()
,一次用于.concatWith()
。 因此,您将有一个重复的第一个元素。
现在,您可能会问,为什么要为所有这些冷热流量烦恼? 为什么不只使用冷焊剂来执行类似以下代码片段的操作? 毕竟感冒会重新生成数据,所以根本不需要调用concatWith()
方法。
Flux<Starship> coldStarshipFlux = starshipService.getFromSpacedock(starshipType);
return coldStarldshipFlux.next()
.flatMap( starShipObj -> // ignore the startShipObj
{
return ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(coldStarldshipFlux, Starship.class);
}
)
.switchIfEmpty(
... //same code as above hot flux
)
.onErrorResume( t ->
{
... //same code as as above hot flux
});
上述代码片段的问题在于,冷通量上的所有订阅都会根据订阅重新生成数据。 is coded, may result in a second HTTP request being made.因此,有效地调用.next()
和.concatWith()
会导致冷通量重新生成相同的数据流,并且根据的编码方式,可能会导致发出第二个 HTTP 请求。 因此,实际上,您将发出两个 HTTP 请求而不是一个。
使用热通量时,可以避免重新生成数据(从而可能会发出第二个 HTTP 请求)的需要。 这是将冷焊剂转化为热焊剂的最大优势。
有关冷热焊剂的详细信息,请参见以下网站。 最后,它非常清楚地解释了热通量和冷通量之间的区别,以及它们的行为方式有何不同:
https://spring.io/blog/2019/03/06/flight-of-the-flux-1-assembly-vs-subscription
我希望这个答案对你有所帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.