【问题标题】:REST endpoint works when directly accessed, but not when accessed via WebClientREST 端点在直接访问时有效,但在通过 WebClient 访问时无效
【发布时间】:2019-12-17 15:02:29
【问题描述】:

注意:以下是对类似帖子/问题的编辑/修订,旨在更好地识别我的问题/问题,并提供更好的代码示例来展示我的问题。

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

我在同一个 Spring 响应式应用程序的两个路由器中有两个端点。第一个 (/v2/DemoPOJO) 似乎工作正常。第二个 (/v2/DemoClient/DemoPOJO) 使用 WebClient 委托给 /v2/DemoPOJO 似乎“什么都不做”(尽管记录的输出显示 DemoClientHandler.add()DemoClient.add() 正在被调用)。

当我向 /v2/DemoPOJO 端点发出 POST 请求时,doFirst()doOnSuccess() >doFinally() 被调用并输出适当的文本(在“现实生活”中,将一行添加到存储库中)。

当我向 /v2/DemoClient/DemoPOJO 端点发出 POST 请求时,它返回 200 OK 状态,但没有输出任何预期的文本(在“现实生活”中,什么都没有添加到存储库)。

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

DemoPOJO 的路由器类实现...

@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 的处理程序类实现...

@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));
    }
}

【问题讨论】:

  • 不要发布重复的问题。编辑您的原始问题以包含所有详细信息。
  • 这有点自相矛盾。编辑原始问题的建议与关于不编辑问题并使现有答案无效的原始问题的建议相冲突。这就是为什么我将原始问题标记为删除,因为我对原始问题做了足够可怕的工作,“保存”它导致编辑到响应没有意义的地步。然后让我想到了重复的问题。 “去”时一切都出错了。

标签: spring-boot spring-webflux reactor-netty


【解决方案1】:

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

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

doOnSuccess 接受一个消费者,一个消费者返回void Mono&lt;Void&gt;

这是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 并返回 Mono&lt;Void&gt; 在这里您打破了链条,因为没有任何东西链接到它返回的 Mono 上,因此它永远不会被订阅,因为它不在事件链中。

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

如果您将其更改为map,它可能会起作用。然后发生的事情是,它正在使用 Mono&lt;DemoPojo&gt; 并将其“映射”到由您的 add 函数返回的 Mono&lt;Void&gt;。突然它在事件链中(回调)。

【讨论】:

  • 实际的解决方案使用了 flatmap (参见修改后的问题),但我终于认识到我的错误在于没有足够注意 lambda 的功能接口的签名.我相信,我需要做更多的事情,才能真正感觉到我正在了解如何做反应链和 lambda,但是......到达那里。感谢您的耐心等待。
猜你喜欢
  • 2021-08-04
  • 2015-10-09
  • 2016-05-26
  • 2019-07-19
  • 1970-01-01
  • 2020-08-16
  • 1970-01-01
  • 2013-02-07
  • 2014-03-17
相关资源
最近更新 更多