簡體   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