[英]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.