【问题标题】:How could I return a different ServerResponse if Flux stream data has error status如果 Flux 流数据有错误状态,我如何返回不同的 ServerResponse
【发布时间】:2020-02-14 05:13:54
【问题描述】:

我正在使用 WebFlux 实现一个 REST 端点,我在处理基本操作时没有任何问题,但是有一个操作我不知道如何管理。 我想返回与 Event 对象相关的 PriceMessage 对象,因此如果事件存在,则端点返回 ServerResponse.ok() 但如果 Event 不存在,则应返回 ServerResponse.notFound()。

在存储库层中有以下方法,如果存在则返回表示代码的 Mono 和带有 eventId 的事件,如果不存在则返回 Mono.empty():

@Override
public Mono<Integer> getSportsEventId(long eventId) {
  Optional<SourceEventDto> optionalSourceEvent =
    springJpaSourceEventRepository.findByEventId(eventId);

  Mono<Integer> result = Mono.empty();

  if (optionalSourceEvent.isPresent()) {
    result = Mono.just(new Integer(optionalSourceEvent.get().getSourceId()));
  }

  return result;
}

然后,服务层返回一个 Flux,如果事件未找到或连接到 Redis 以获取与 sportsEventId 相关的价格,则设置错误:

@Override
public Flux<PriceMessage> getPrices(long eventId) {
  return
    // get the sportsEventId
    sourceEventRepository.getSportsEventId(eventId)
      // notify the event does not exist
      .switchIfEmpty(Mono.error(new IllegalStateException("Event " + eventId + " does not exist")))
      // get the related PriceEntity objects
      .flatMapMany(priceRepository::findBySporsEventId)
      // transform to PriceMessage
      .map(priceMessageFactory::from);
}

此代码经过单元测试,我还对其进行了调试,以确保在流中设置错误时不会调用 flatMapMany 和地图部分。

最后,在 REST 层有这段代码,幸福之路:

public Mono<ServerResponse> getPricesByEventId(ServerRequest request) {
  String eventIdParam = request.pathVariable("eventId");

  // call the service layer
  Flux<PriceMessage> prices = priceService.getPrices(eventId);

  return
    ServerResponse.ok()
      .contentType(MediaType.APPLICATION_STREAM_JSON)
      .body(prices, PriceMessage.class);
}

这工作正常,但如果价格流包含错误,因为未找到事件,我不知道如何返回 404 错误,在正文中设置错误消息,如下所示:

ServerResponse.status(HttpStatus.NOT_FOUND)
  .contentType(MediaType.APPLICATION_JSON_UTF8)
  .syncBody(<error_message_from_flux>);

伪代码是这样的,但我无法将其转换为响应式:

if (!prices.isError()) then
  return
    ServerResponse.ok()
      .contentType(MediaType.APPLICATION_STREAM_JSON)
      .body(prices, PriceMessage.class);
else
  return
    ServerResponse.status(HttpStatus.NOT_FOUND)
     .contentType(MediaType.APPLICATION_JSON_UTF8)
     .syncBody(<error_message_from_flux>);
end

任何帮助将不胜感激。

【问题讨论】:

    标签: spring-webflux


    【解决方案1】:

    您可以使用 onErrorResume 创建错误响应以防发生错误。

     return priceService.getPrices(eventId).flatMap(prices -> ServerResponse.ok()
              .contentType(MediaType.APPLICATION_STREAM_JSON)
              .body(prices, PriceMessage.class))
           .onErrorResume(err -> ServerResponse.status(HttpStatus.NOT_FOUND).body({Your Error Body Here}).build());
    

    【讨论】:

    • flatMap 和 onErrorResume 操作都返回 Flux 而不是 Mono。无论如何,flatMap 代码将被调用多次,因为返回的价格不是预期的行为。
    • 发生错误后不会调用flatMap
    【解决方案2】:

    注册路由时可以使用choose来处理。

    @Bean
    public RouterFunction<ServerResponse> route(PriceMessageHandler handler) {
      return RouterFunctions.route().GET("/",
          request -> ServerResponse.ok().body(handler.handle(request), PriceMessage.class))
          .onError(Exception.class, (e, a) -> ServerResponse.status(HttpStatus.NOT_FOUND).build())
          .build();
    }
    

    或者您可以在 ServerResponse 上使用 onErrorResume:

    return ServerResponse.ok().body(priceService.getPrices(eventId), PriceMessage.class).onErrorResume((e) -> ServerResponse.notFound().build());
    

    希望这会有所帮助。

    【讨论】:

    • 此解决方案与前一个解决方案存在相同的问题。 WebFlux 期望返回 Mono,而它的主体可以是 Flux(可以为空)或包含错误消息的字符串。
    • 如果您想要单声道,您可以通过priceService.getPrices(eventId).collectList() 将通量响应转换为单声道。我已经相应地更新了我的答案。
    • 在完成所有价格序列以创建 PriceMessage 列表之前,不会阻止 collectList 吗?预计价格数量将高于 50,000,整个想法是在可用后立即将它们一一发送。在这个解决方案中,延迟会受到影响,不是吗?
    猜你喜欢
    • 1970-01-01
    • 2021-09-09
    • 2020-01-05
    • 2022-01-15
    • 2021-09-28
    • 2021-03-01
    • 2019-08-25
    • 1970-01-01
    • 2019-09-11
    相关资源
    最近更新 更多