首页 > 解决方案 > WebClient 未成功调用“POST”操作

问题描述

我正在玩 Spring 的WebClient。REST 端点的主要实现(在DemoPOJORouterDemoPOJOHandler 中)似乎有效。此外,DemoClientRouterDemoClientHandler中的http.Get端点似乎可以工作。

但是,DemoClient实现的 http.Post “什么都不做”。它返回成功(200),但没有任何内容添加到虚拟存储库中。我有一种感觉,我需要在 DemoClient 中做一些事情以使 DemoPOJOHandler 中的http.Post端点实际执行(即,我相信DemoPOJOService.add()DemoPOJORepo.add()中的语句都没有被执行)。

基于之前在 WebFlux/reactive/functional 工作中的错误,我有一种我没有成功订阅的感觉,因此这些语句永远不会被调用。但是,我很难确定“为什么”。

测试代码如下...

DemoClient 路由器...

@Configuration
public class DemoClientRouter {

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

DemoClient 处理程序...

@Component
public class DemoClientHandler {

    public static final String PATH_VAR_ID = "id";

    @Autowired
    DemoClient demoClient;

    public Mono<ServerResponse> getById(ServerRequest request) {
        Mono<DemoPOJO> monoDemoPOJO;
        int            id;

        // short-circuit if bad request or invalid value for id
        id = getIdFromServerRequest(request);
        if (id < 1) {
            return ServerResponse.badRequest().build();
        }

        // non-blocking mechanism for either returning the Mono<DemoPOJO>
        //  or an empty response if Mono<Void> was returned by repo.getById()
        return demoClient.getById(id).flatMap(demoPOJO -> ServerResponse.ok()
                                                                        .contentType(MediaType.APPLICATION_JSON)
                                                                        .body(Mono.just(demoPOJO), DemoPOJO.class))
                                                                        .switchIfEmpty(ServerResponse.notFound().build());
    }

    public Mono<ServerResponse> add(ServerRequest request) {
        return request.bodyToMono(DemoPOJO.class).doOnSuccess( demoPOJO -> demoClient.add(demoPOJO))
                                                     .then(ServerResponse.ok().build())
                                                 .onErrorResume(e -> simpleErrorReporter(e))
                                                 .switchIfEmpty(ServerResponse.badRequest().build());
    }

    private int getIdFromServerRequest(ServerRequest request) {
        Map<String, String> pathVariables = request.pathVariables();
        int                 id            = -1;

        // short-circuit if bad request
        //  should never happen, but if this method is ever called directly (vice via DemoPOJORouter)
        if ((pathVariables == null)
         || (!pathVariables.containsKey(PATH_VAR_ID))) {
            return id;
        }

        try {
            id = Integer.parseInt(pathVariables.get(PATH_VAR_ID));
        } catch (NumberFormatException e) {
            // swallow the error, return value <0 to signal error
            id = -1;
        }
        return id;
    }

    private Mono<ServerResponse> simpleErrorReporter(Throwable e) {
        return ServerResponse.badRequest()
                             .contentType(MediaType.TEXT_PLAIN)
                             .syncBody(e.getMessage());
    }
}

DemoClient 实现...

@Component
public class DemoClient {

    private final WebClient client;

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

    public Mono<DemoPOJO> getById(int id) {
        return client.get().uri("http://localhost:8080/v2/DemoPOJO/" + id)
                           .accept(MediaType.APPLICATION_JSON)
                           .exchange()
                           .flatMap(response -> response.bodyToMono(DemoPOJO.class));
    }

    public Mono<Boolean> add(DemoPOJO demoPOJO) {
        return client.post().uri("http://localhost:8080/v2/DemoPOJO")
                            .syncBody(demoPOJO)
                            .exchange()
                            .flatMap(response -> response.bodyToMono(Boolean.class));
    }
}

而且,DemoPOJO 的东西,从DemoPOJORouter开始......

@Configuration
public class DemoPOJORouter {

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

演示POJO处理程序...

@Component
public class DemoPOJOHandler {

    public static final String PATH_VAR_ID = "id";

    @Autowired
    private DemoPOJOService service;

    public Mono<ServerResponse> getById(ServerRequest request) {
        Mono<DemoPOJO> monoDemoPOJO;
        int            id;

        // short-circuit if bad request or invalid value for id
        id = getIdFromServerRequest(request);
        if (id < 1) {
            return ServerResponse.badRequest().build();
        }

        // non-blocking mechanism for either returning the Mono<DemoPOJO>
        //  or an empty response if Mono<Void> was returned by repo.getById()
        return service.getById(id).flatMap(demoPOJO -> ServerResponse.ok()
                                                                     .contentType(MediaType.APPLICATION_JSON)
                                                                     .body(Mono.just(demoPOJO), DemoPOJO.class))
                                  .switchIfEmpty(ServerResponse.notFound().build());
    }

    public Mono<ServerResponse> add(ServerRequest request) {
        return request.bodyToMono(DemoPOJO.class).doOnSuccess( demoPOJO -> service.add(demoPOJO))
                                                     .then(ServerResponse.ok().build())
                                                 .onErrorResume(e -> simpleErrorReporter(e))
                                                 .switchIfEmpty(ServerResponse.badRequest().build());
    }

    private int getIdFromServerRequest(ServerRequest request) {
        Map<String, String> pathVariables = request.pathVariables();
        int                 id            = -1;

        // short-circuit if bad request
        //  should never happen, but if this method is ever called directly (vice via DemoPOJORouter)
        if ((pathVariables == null)
         || (!pathVariables.containsKey(PATH_VAR_ID))) {
            return id;
        }

        try {
            id = Integer.parseInt(pathVariables.get(PATH_VAR_ID));
        } catch (NumberFormatException e) {
            // swallow the exception, return illegal value to signal error
            id = -1;
        }
        return id;
    }

    private Mono<ServerResponse> simpleErrorReporter(Throwable e) {
        return ServerResponse.badRequest()
                             .contentType(MediaType.TEXT_PLAIN)
                             .syncBody(e.getMessage());
    }
}

演示POJO服务...

@Component
public class DemoPOJOService {

    @Autowired
    private DemoPOJORepo demoPOJORepo;

    public Mono<DemoPOJO> getById(int id) {
        DemoPOJO demoPOJO = demoPOJORepo.getById(id);

        return (demoPOJO == null) ? Mono.empty()
                                  : Mono.just(demoPOJO);
    }

    public Mono<Boolean> add(DemoPOJO demoPOJO) {
        return Mono.just(demoPOJORepo.add(demoPOJO));
    }
}

DemoPOJORepo ...

@Component
public class DemoPOJORepo {

    private static final int NUM_OBJS = 5;

    private static DemoPOJORepo demoRepo = null;

    private Map<Integer, DemoPOJO> demoPOJOMap;

    private DemoPOJORepo() {
        initMap();
    }

    public static DemoPOJORepo getInstance() {
        if (demoRepo == null) {
            demoRepo = new DemoPOJORepo();
        }
        return demoRepo;
    }

    public DemoPOJO getById(int id) {
        return demoPOJOMap.get(id);
    }

    public boolean add(DemoPOJO demoPOJO) throws InvalidParameterException {
        // short-circuit on null pointer or duplicate id
        if (demoPOJO == null) {
            throw new InvalidParameterException("Add failed, null object detected...");
        } else if (demoPOJOMap.containsKey(demoPOJO.getId())) {
            throw new InvalidParameterException("Add failed, duplicate id detected...");
        }

        demoPOJOMap.put(demoPOJO.getId(), demoPOJO);
        // if the return statement is reached, then the new demoPOJO was added
        return true;
    }
}

最后,DemoPOJO ...

public class DemoPOJO {

    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(int id) {
        this(id, DEF_NAME, DEF_VALUE);
    }

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

    /*
     * setters and getters go here
     */

    public String toString() {
        StringBuilder builder = new StringBuilder();

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

标签: spring-bootspring-webfluxproject-reactor

解决方案


这可能是你的问题。

DemoPOJOHandler.class

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

DemoPOJOService.class

public Mono<Boolean> add(DemoPOJO demoPOJO) {
    return Mono.just(demoPOJORepo.add(demoPOJO));
}

doOnSuccessVoidreturn ,但是您正在调用一个将“动作”包装在返回中的方法Mono。所以这个demoPOJORepo#add函数永远不会被触发,因为你已经破坏了这里的事件链。最简单的解决方法是移除包装Mono并返回 void。

public void add(DemoPOJO demoPOJO) {
    demoPOJORepo.add(demoPOJO);
}

这花了我很长时间才找到,所以这里有一些提问时的提示。

  • 你们的类名太像了,很难按照代码流来
  • DemoPOJOService service你们的名字太像了,所以当我看到service是 DemoPOJOService 还是 DemoClientService 时?请明确名称。
  • 当你写到我不知道你在说什么时,没有什么叫做http.POST 。
  • 您的部件有问题,POST但您发布了所有内容,甚至是工作GET部件,请仅发布您怀疑相关的代码并且是问题的一部分。
  • 更清楚地解释问题,你做了什么,你是怎么做的,你的应用程序结构是什么等等
  • 您的端点网址什么也没说“/DemoClient”?

这个问题如何被问得更清楚:

我在同一个弹簧反应应用程序的两个路由器中有两个端点。

当我向“/add”端点发出 POST 请求时,该端点又使用 WebClient 对另一个名为“/addToMap”的端点上的同一应用程序进行 POST 调用。

当第一次调用返回时,它返回 200 OK 状态,但是当我检查地图(第二个端点应该将发布的数据添加到)时,没有添加任何内容。

所以请下次问问题时,要清楚,非常清楚,比你想象的要清楚得多。确保您的代码也清晰,具有良好的变量和类名称以及清晰的 url 名称。如果您自己的计算机上的名称混乱,那很好,但在此处发布时请保持礼貌并清理代码。为类和参数添加好名称需要 5 分钟,以便我们更快地理解您的代码。

请花时间阅读“如何提出一个好问题”。

如何提出一个好问题


推荐阅读