REST 端点在直接访问时有效,但在通过 WebClient 访问时无效

REST endpoint works when directly accessed, but not when accessed via WebClient

注意:下面是一个edit/revision类似的post/question,试图更好地识别我的issue/question,并提供更好的代码示例来说明我的问题。

添加注释:代码示例已修改为包含工作代码。

我在同一个 spring 响应式应用程序的两个路由器中有两个端点。第一个 (/v2/DemoPOJO) 似乎工作正常。第二个(/v2/DemoClient/DemoPOJO),它使用 WebClient 委托给 /v2/DemoPOJO 似乎 "do nothing"(尽管记录的输出显示 DemoClientHandler.add()DemoClient.add() 正在被调用。

当我向 /v2/DemoPOJO 端点发出 POST 请求时,doFirst()doOnSuccess()doFinally() 被调用并输出适当的文本(在 "real life" 中,一行被添加到存储库中) .

当我向 /v2/DemoClient/DemoPOJO 端点发出 POST 请求时,它 returns 200 OK 状态,但是 none 的预期文本被输出(在 "real life" 中,没有任何内容被添加到存储库中)。

以下文件支持 /v2/DemoPOJO 端点...

DemoPOJO 的路由器 class 实现...

@Configuration
public class DemoPOJORouter {
    private Logger logger = LoggerFactory.getLogger(DemoPOJORouter.class);

    @Bean
    public RouterFunction<ServerResponse> demoPOJORoute(DemoPOJOHandler requestHandler) {
        logger.debug("DemoPOJORouter.demoPOJORoute( DemoPOJOHandler )");
        return nest(path("/v2"),
                nest(accept(APPLICATION_JSON),
                        RouterFunctions.route(RequestPredicates.POST("/DemoPOJO"), requestHandler::add)));
    }
}

DemoPOJO 的处理程序class 实现...

@Component
public class DemoPOJOHandler {
    private Logger logger = LoggerFactory.getLogger(DemoPOJOHandler.class);

    public Mono<ServerResponse> add(ServerRequest request) {
        logger.debug("DemoPOJOHandler.add( ServerRequest )");

        return request.bodyToMono(DemoPOJO.class).doFirst(() -> System.out.println("-> doFirst()."))
                                                 .doOnSuccess(demoPOJO -> System.out.println("Received >> " + demoPOJO.toString()))
                                                     .then(ServerResponse.accepted().build())
                                                 .doOnError(e -> System.out.println("-> doOnError()"))
                                                 .doFinally(demoPOJO -> System.out.println("-> doFinally()"));
    }
}

DemoPOJO 实现j...

public class DemoPOJO {
    private Logger logger = LoggerFactory.getLogger(DemoPOJO.class);

    public static final String DEF_NAME  = "DEFAULT NAME";
    public static final int    DEF_VALUE = 99;

    private int    id;
    private String name;
    private int    value;

    public DemoPOJO(@JsonProperty("id") int id, @JsonProperty("name") String name, @JsonProperty("value") int value) {
        logger.debug("DemoPOJO.DemoPOJO( {}, {}, {} )", id, name, value);
        this.id = id;
        this.name = name;
        this.value = value;
    }

    /*
     * setters and getters go here
     */

    public String toString() {
        logger.debug("DemoPOJO.toString()");
        StringBuilder builder = new StringBuilder();

        builder.append(id);
        builder.append(" :: ");
        builder.append(name);
        builder.append(" :: ");
        builder.append(value);
        return builder.toString();
    }
}

以下文件支持 /v2/DemoClient/DemoPOJO 端点...

DemoClient 的路由器实现...

@Configuration
public class DemoClientRouter {
    private Logger logger = LoggerFactory.getLogger(DemoClientRouter.class);

    @Bean
    public RouterFunction<ServerResponse> clientRoutes(DemoClientHandler requestHandler) {
        logger.debug("DemoClientRouter.route( DemoClientHandler )");
        return nest(path("/v2/DemoClient"),
                nest(accept(APPLICATION_JSON),
                        RouterFunctions.route(RequestPredicates.POST("/DemoPOJO"), requestHandler::add)));
    }
}

DemoClient 的处理程序实现...

@Component
public class DemoClientHandler {
    private Logger logger = LoggerFactory.getLogger(mil.navy.demo.demopojo.DemoPOJOHandler.class);

    @Autowired
    DemoClient demoClient;

    public Mono<ServerResponse> add(ServerRequest request) {
        logger.debug("DemoClientOHandler.add( ServerRequest )");

        // THIS CODE
        return request.bodyToMono(DemoPOJO.class).flatMap(demoPOJO -> demoClient.add(demoPOJO))
                                                 .then(ServerResponse.accepted().build());

        // REPLACES THIS CODE
        /*
           return request.bodyToMono(DemoPOJO.class).doOnSuccess( demoPOJO -> demoClient.add(demoPOJO))
                                                     .then(ServerResponse.ok().build())
                                                 .switchIfEmpty(ServerResponse.badRequest().build());
         */
    }
}

DemoClient 的 WebClient 实现...

@Component
public class DemoClient {
    private Logger logger = LoggerFactory.getLogger(DemoClient.class);

    private final WebClient client;

    public DemoClient() {
        client = WebClient.create();
    }

    public Mono<Boolean> add(DemoPOJO demoPOJO) {
        logger.debug("DemoClient.add( DemoPOJO )");

        logger.debug("DemoClient.add() >> DemoPOJO -> {}", demoPOJO.toString());
        return client.post().uri("http://localhost:8080/v2/DemoPOJO")
                            .accept(MediaType.APPLICATION_JSON)
                            .syncBody(demoPOJO)
                            .exchange()
                            .flatMap(response -> response.bodyToMono(Boolean.class));
    }
}

这就是我猜你的问题所在。

return request.bodyToMono(DemoPOJO.class)
    .doOnSuccess( demoPOJO -> demoClient.add(demoPOJO))

doOnSuccess取一个消费者,一个消费者returnsvoidnotMono<Void>.

这里是 Consumer.

的详细用法
Mono.just("hello")
    .doOnSuccess(new Consumer<String>() {

        @Override
        public void accept(String s) {

            // See here, it returns void

        }
    });

让我们看一些例子:

Mono<String> helloWorld = Mono.just("Hello")
    .doOnSuccess(string -> {
        // This will never be executed because 
        // it is just declared and never subscribed to
        Mono.just(string + " world");
    });

helloWorld.doOnSuccess(string -> {
        // This will print out Hello
        System.out.println(string);
    });

 Mono<String> hello = Mono.just("Hello")
        .doOnSuccess(string -> {
        // This will print out Hello World
        System.out.println(string + " World");
    });

 // hello hasn't been changed     
 hello.map(string -> {
        // This will also print out Hello World
        System.out.println(string + " World");
    });

 // This prints hello world to after we mapped it
 Mono<String> helloworld = Mono.just("hello")
     .map(s -> s + " World")
     .doOnSuccess(System.out::println);

 // Now this is what you are essentially doing
 // See how this is wrong?
 Mono<DemoPOJO> demoPOJO = request.bodyToMono(DemoPOJO.class)
    .doOnSuccess( demoPOJO -> Mono.empty() );

你正在调用 demoClient#add 那 returns a Mono<Void> 这里你打破了链条,因为没有任何东西链接到它返回的 Mono 上,所以它永远不会被订阅,因为它不在事件链中。

return request.bodyToMono(DemoPOJO.class)
    .map( demoPOJO -> {
        return demoClient.add(demoPOJO);
    });

如果将其更改为 map,它可能会起作用。然后发生的事情是它将 Mono<DemoPojo> 和 "mapping" 带到 add 函数返回的 Mono<Void>。突然它在事件链中(回调)。