spring-boot - WebClient 未成功调用“POST”操作
问题描述
我正在玩 Spring 的WebClient。REST 端点的主要实现(在DemoPOJORouter和DemoPOJOHandler 中)似乎有效。此外,DemoClientRouter和DemoClientHandler中的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();
}
}
解决方案
这可能是你的问题。
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));
}
doOnSuccess
Void
return ,但是您正在调用一个将“动作”包装在返回中的方法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 分钟,以便我们更快地理解您的代码。
请花时间阅读“如何提出一个好问题”。
推荐阅读
- android - 结合两个函数时出现错误(元素重复)
- javascript - Bootstrap 文件输入 - 如何在初始预览中隐藏拖动按钮?
- html - 如何使 html 文件在 Wildfly tmp 目录中可用?
- c - C - malloc 用于指向结构的指针
- cqrs - 带有事件溯源的逻辑删除(可能带有敏感数据/GDPR)
- angular - 当我在输入字段中按回车键时,角度重定向
- javascript - Firebase 云函数错误:函数返回未定义、预期的 Promise 或值
- php - 无法使用 array_map 执行爆炸
- android - 对话框活动 - 自定义主题
- swift - 未找到匹配项:与输入中的谓词“”“IN 标识符”匹配的元素