首页 > 解决方案 > Spring WebClient odd behaviour

问题描述

I am using the webClient to execute an http request using the following code

  @Bean
    public CommandLineRunner commandLineRunner() {
        return args -> Flux.just(1)
            .flatMap(__ -> WebClient
                .builder()
                .build()
                .post()
                .uri("http://thisuridoesnotexist/")
                .exchangeToMono(response -> Mono.just(1))
                .doOnError(throwable -> System.out.println("1"))
                .onErrorResume(
                    Exception.class,
                    throwable -> {
                        System.out.println("2");
                        return Mono.error(new RuntimeException("This vanishes"));
                    })
                .onErrorContinue((throwable, o) -> {
                    System.out.println("8");
                }))
            .doOnError(__ -> System.out.println("9"))
            .onErrorContinue((throwable, obj) -> System.out.println("10"))
            .subscribe();
    }

The issue actually occurs when the webClient is trying to speak with a uri that is not answering and it throws a connection refused exception. As you can see in the code there are multiple Xerror operators (which were placed just for the PoC of course ) But the only thing actually working here is the onErrorContinue with the 8. It is expected for the 9 and 10 not to work as the "error" is resolved on the 8 but! if I comment out the 8 then it skips 9 and goes directly to 10. Lastly no matter the case it goes always skips the 1 and 2 operators.

Reading the documentation of reactor and the javadoc above doOnError let say, it clearly states that for the Xerror operators it cathces any exceptions which does not happen here.

Last but not least removing the onErrorContinue the 1, 2 and 9 work as expected. So if the onErrorContinue is the issue that just consumes everything eagerly how can I use a "catchAll" failsafe incase an error is not "predicted" on specific cases or its just handled wrong?

just for the completion of the code the pom used in this demo-project is the following

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

标签: javaspringwebclientreactor

解决方案


根据文档描述,这不是 Spring 的行为,而是onErrorContinue

https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html#onErrorContinue-java.util.function.BiConsumer-

通过从序列中删除犯罪元素并继续后续元素,让上游兼容的运算符从错误中恢复。通过提供的 BiConsumer 通知恢复的错误和相关值。或者,从该双消费者抛出的异常将向下游传播抛出的异常,以代替原始错误,该异常作为抑制异常添加到新错误中。

...

请注意,onErrorContinue() 是一个专业运算符,可以使您的反应链的行为不清楚。它在上游而不是下游运营商上运行,它需要特定的运营商支持才能工作,并且范围可以轻松地将上游传播到没有预料到的库代码中(导致意外行为。)

所以 doOnError 没有收到异常,因为上游已恢复。

为了理解它的真正含义以及如何处理它,我做了一些实验并发现,如果你把它throw new RuntimeException()放在块81并被2抓住。但不是9, 因为相同的行为10


推荐阅读