spring 通过Mono从另一个WebClient调用mono过滤来自WebClient的通量

9rygscc1  于 2023-03-07  发布在  Spring
关注(0)|答案(1)|浏览(148)

我有一个有两个端点的公共API,我必须从第一个GET端点获取一个对象数组作为Flux,然后调用第二个(内部调用)GET端点Mono(Mono包含对象列表),在那里我必须通过id找到一个内部对象,然后使用找到的对象(列表)过滤器第一个Flux(每个都包含列表)
我试着用这种方式,但它变成了一个永远的流

public class Test {

    String baseUrl = "http://some-site.com/";
    private final WebClient webClient = WebClient.create(baseUrl);

    @SneakyThrows
    public Flux<One> getObjOneFlux(String id) {
        Flux<One> oneFlux = loadObjOnes();
        Mono<Two> twoMono = loadObjTwo();

        return oneFlux.filterWhen(one -> twoMono.map(two -> one.getOneInternalList()
                        .stream()
                        .map(One.OneInternal::getName).toList()
                        .containsAll(
                                two.getTwoInternalList().stream()
                                        .filter(s -> s.getId().equals(id))
                                        .findFirst().get().getOptions()
                        )
                )
        );
    }

    public Flux<One> loadObjOnes() {
        return webClient.get()
                .uri(uriBuilder -> uriBuilder.path("/ObjOne").build())
                .exchange()
                .flatMap(res -> res.bodyToMono(ObjOneResponse.class))
                .flatMapIterable(res -> res)
                .map(t ->
                        One.builder()
                                .id(t.getId())
                                .mark(t.getMark())
                                .oneInternalList(t.getOneInternalList())
                                .build());
    }

    public Mono<Two> loadObjTwo() {
        return webClient.get()
                .uri(uriBuilder -> uriBuilder.path("/ObjTwo").build())
                .exchange()
                .flatMap(res -> res.bodyToMono(Two.class))
                .map(s -> Two.builder()
                        .twoInternalList(s.getTwoInternalList())
                        .build()).delayElement(Duration.ofSeconds(3));
    }
}

@Data
@Builder
class One {
    private String id;
    private String mark;
    private List<OneInternal> oneInternalList;

    @Data
    @Builder
    public static final class OneInternal {
        private String name;
    }
}

@Data
@Builder
class Two {
    private List<TwoInternal> twoInternalList;

    @Data
    @Builder
    public static final class TwoInternal {
        private List<String> options;
        private String id;
    }
}

class ObjOneResponse extends ArrayList<One> {

}
3z6pesqy

3z6pesqy1#

你的问题是什么?实现看起来不错,至少对于快乐流来说。看,我已经提取了变量namesFromOneoptionsFromTwo

oneFlux.filterWhen(one -> twoMono.map(two -> {
            List<String> namesFromOne = one.getOneInternalList()
                    .stream()
                    .map(One.OneInternal::getName).toList();
            List<String> optionsFromTwo = two.getTwoInternalList().stream()
                    .filter(s -> s.getId().equals(id))
                    .findFirst().get().getOptions();
            return namesFromOne.containsAll(optionsFromTwo);
        })
);

我看到唯一的问题是.findFirst().get()。理想情况下,您希望在那里返回false,而不是抛出异常,因此您可以做的事情如下所示:

Optional<Two.TwoInternal> optionalTwoInternal = two.getTwoInternalList().stream()
        .filter(s -> s.getId().equals(id))
        .findFirst();

return optionalTwoInternal
        .map(twoInternal -> namesFromOne.containsAll(twoInternal.getOptions()))
        .orElse(false);

一些小/简单测试:

@Test
void test() {
    Flux<One> oneFlux = Flux.just(
            createOne("id1", "a", "b"),
            createOne("id2", "c", "d"));

    Mono<Two> twoMono = Mono.just(Two.builder()
            .twoInternalList(List.of(
                    createTwoInternal("id1", "a", "b"),
                    createTwoInternal("id2", "e", "f"),
                    createTwoInternal("id4", "g", "h")
            )).build());

    var resp1 = getObjOneFlux(oneFlux, twoMono, "id1").collectList().block();
    System.err.println(resp1); // prints first element of oneFlux

    var resp2 = getObjOneFlux(oneFlux, twoMono, "id2").collectList().block();
    System.err.println(resp2); // prints empty array
}

正如我所看到的,你的过滤工作。也许你可以考虑一下你的模型,因为TwoInternal看起来很像类One,所以也许你可以重用那里的一些东西。

相关问题