繁体   English   中英

在 Flux.error 的情况下返回相关的 ServerResponse

[英]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 上使用 flatMap,但这会导致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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM