【问题标题】:Spring Boot Webflux - flatMap is the correct way to chain http calls?Spring Boot Webflux - flatMap 是链接 http 调用的正确方法吗?
【发布时间】:2021-08-30 07:41:42
【问题描述】:

关于 Spring Webflux 的小问题,以及如何“链接”http 调用。

通过一个具体的例子,这里是一个非常简单的 Spring MVC 示例,带有一个 rest 模板。

RestTemplate restTemplate = new RestTemplate();

        HttpEntity<StepOneRequest> stepOneRequestHttpEntity = new HttpEntity<>(new StepOneRequest("foo", "bar"));
        StepOneResponse stepOneResponse = restTemplate.postForObject("https://step-one-web-service:443/getStepTwoFromFooBar", stepOneRequestHttpEntity, StepOneResponse.class);

        HttpEntity<StepTwoRequest> stepTwoRequestHttpEntity = new HttpEntity<>(new StepTwoRequest(stepOneResponse.getTheDependencyFromStepOne()));
        StepTwoResponse stepTwoResponse = restTemplate.postForObject("https://step-two-web-service:443/getResponseFromStepTwo", stepTwoRequestHttpEntity, StepTwoResponse);

        return ResponseEntity.ok(new MyResponse(stepTwoResponse.getImportantValueFromStepTwo()));
    }

在这个sn-p中,我们看到的很直接。 仅初始化一个休息模板。

http 请求负载对象的构造。

使用构造的对象查询第一个外部 Web 应用程序 API 以获取响应。

重要的是,需要第一个 HTTP 调用的响应才能进行第二个 HTTP 调用。它们只能是顺序的,它需要第一次调用的结果,才能查询第二个 API。

然后,对第二个 API 的 http 调用。

最后,根据第二个 API 响应原始请求者。

现在,如果将其转换为 Webflux:

WebClient webClientStepOne = WebClient.create("https://step-one-web-service:443/getStepTwoFromFooBar");
        WebClient webClientStepTwo = WebClient.create("https://step-two-web-service:443/getResponseFromStepTwo");

        Mono<StepOneResponse> stepOneResponseMono = webClientStepOne.post().body(BodyInserters.fromValue(new StepOneRequest("foo", "bar"))).retrieve().bodyToMono(StepOneResponse.class);

        Mono<StepTwoResponse> stepTwoResponseMono = stepOneResponseMono.flatMap(aStepOneResponse -> webClientStepTwo.post().body(BodyInserters.fromValue(aStepOneResponse.getTheDependencyFromStepOne())).retrieve().bodyToMono(StepTwoResponse.class));

        return stepTwoResponseMono.map(aStepTwoResponse -> ResponseEntity.ok(new MyResponse(aStepTwoResponse.getImportantValueFromStepTwo())));
    }

我有一个大问题。虽然这是正确的(它产生相同的响应)这是正确的做法吗?

尤其是带有 flatMap 的线

Mono<StepTwoResponse> stepTwoResponseMono = stepOneResponseMono.flatMap(aStepOneResponse -> webClientStepTwo.post().body(BodyInserters.fromValue(aStepOneResponse.getTheDependencyFromStepOne())).retrieve().bodyToMono(StepTwoResponse.class));

这似乎很不合时宜。这里有更好的方法吗?

另外,还有一个问题,如果我需要链接 N 个 api 调用,我们真的需要 N 个 WebClient 吗? URL 不同,不仅仅是路径,完整的 URL。当rest模板只能处理一个实例时,这里似乎,如果我需要调用N个外部服务,我需要N个WebClient。

请问在这里使用 WebClient 的正确方法是什么? 请问链接http调用的正确方法是什么?

谢谢

【问题讨论】:

    标签: java spring-webflux


    【解决方案1】:

    我有一个大问题。虽然这是正确的(它产生相同的响应)这是正确的做法吗?

    基本上,是的,你做对了。唯一不正确的是您的 Web 客户端使用情况和您的风格。

    你当然不必为每个 URI 指定一个 WebClient(你不应该,它们应该是可重用的。)所以如果你有不同的域和相同的 webclient,只需创建一个标准实例:

    WebClient wc = WebClient.create();
    

    ...然后使用:

    webClientStepOne.post()
                    .uri("https://blah")
    

    ...在链的顶部指定具有相同实例的不同 URI。

    就风格而言 - 您会注意到,当将代码拆分为单独的变量并写成长行时,代码会很快变得笨拙。乍一看似乎违反直觉,反应式领域普遍接受的最佳实践是在单个语句中完成所有操作,在单独的行上格式化每个新方法调用(使您能够阅读然后查看反应链的执行情况。)所以在你的情况下,它会变成:

    return wc.post().uri("https://step-one-web-service:443/getStepTwoFromFooBar")
            .body(BodyInserters.fromValue(new StepOneRequest("foo", "bar")))
            .retrieve()
            .bodyToMono(StepOneResponse.class)
            .flatMap(aStepOneResponse ->
                    wc.post().uri("https://step-two-web-service:443/getResponseFromStepTwo")
                            .body(BodyInserters.fromValue(aStepOneResponse.getTheDependencyFromStepOne()))
                            .retrieve()
                            .bodyToMono(StepTwoResponse.class)
            )
            .map(aStepTwoResponse ->
                    ResponseEntity.ok(new MyResponse(aStepTwoResponse.getImportantValueFromStepTwo()))
            );
    

    也许你可以稍微整理一下,例如将内部平面图调用委托给一个单独的方法 - 但这实际上归结为偏好。

    【讨论】:

      猜你喜欢
      • 2021-01-18
      • 2019-04-24
      • 2017-09-20
      • 1970-01-01
      • 1970-01-01
      • 2016-05-10
      • 2017-07-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多