Spring WebFlux中WebClient调用未触发问题咨询
作为响应式编程新手,在Spring WebFlux中遇到WebClient调用未触发的问题:Rest资源类通过@PostMapping暴露接口,调用服务层方法。当服务层返回httpCall(userResourceDTO)的Mono对象时,HTTP请求可正常触发;但仅定义Mono变量却返回其他Mono值时,WebClient请求完全不触发。此外,twocalls方法中httpCall后的flatMap逻辑也未执行。
相关代码
Rest资源类代码
@PostMapping public Mono<String> createUser(@RequestBody UserResourceDTO userResourceDto) { return userService.createUser(userResourceDto); }
工作正常的服务层方法
// Working method public Mono<String> createUser(UserResourceDTO userResourceDTO){ return httpCall(userResourceDTO); }
未触发请求的服务层方法
// Not Working method public Mono<String> createUser(UserResourceDTO userResourceDTO){ Mono<String> rs = httpCall(userResourceDTO); return Mono.just("just a value"); // 后续计划触发数据库调用,因此不想返回httpCall结果 // return userRepository.createUser(userResourceDTO); }
httpCall方法代码
public Mono<String> httpCall(UserResourceDTO userResourceDTO) { WebClient webClient = WebClient.create(); KeyCloakUser keyCloakUser = new KeyCloakUser(); // 设置参数... return webClient.post() .uri(new URI("myurl")) .header("Authorization", "Bearer "+toremove) .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .body(Mono.just(keyCloakUser), KeyCloakUser.class) .retrieve() .bodyToMono(String.class); }
未执行的twocalls方法
public Mono<UserResourceDTO> twocalls(UserResourceDTO userResourceDTO){ return httpCall(userResourceDTO) .flatMap(res -> { // 该输出未执行 System.out.println(res); // 接口返回201且响应体为空 if("".equals(res)) return userRepository.createUser(userResourceDTO); }); }
问题:为何第二种createUser方法无法触发WebClient调用,为何必须返回httpCall的结果?
1. 第二种createUser方法不触发调用的原因
这是响应式编程的核心特性——惰性求值。Mono/Flux只是描述了异步操作的"执行蓝图",只有当这个Mono/Flux被订阅(subscribe)时,对应的操作才会真正执行。
在工作正常的方法中,你返回了httpCall的Mono,Spring WebFlux框架会自动订阅这个返回的Mono,从而触发WebClient的HTTP请求。
但在未工作的方法里,你只是创建了httpCall的Mono对象rs,但这个Mono从未被订阅——你返回的是Mono.just("just a value"),框架只会订阅这个Mono,和rs没有任何关联,所以httpCall对应的HTTP请求根本不会被触发。
2. 如何触发httpCall同时返回其他结果?
如果你想执行httpCall但不想返回它的结果(比如后续要返回数据库操作结果),需要把两个操作关联起来,确保httpCall的Mono被订阅。可以用以下两种方式:
方式一:用then忽略httpCall结果,执行后续操作
public Mono<String> createUser(UserResourceDTO userResourceDTO){ // 先执行httpCall,完成后执行数据库操作,返回数据库操作的结果 return httpCall(userResourceDTO) .then(userRepository.createUser(userResourceDTO)); }
方式二:用flatMap基于httpCall结果做判断
public Mono<String> createUser(UserResourceDTO userResourceDTO){ return httpCall(userResourceDTO) .flatMap(httpResult -> { // 可根据httpResult做逻辑判断 return userRepository.createUser(userResourceDTO); }); }
这样框架订阅最终返回的Mono时,会先触发httpCall的执行,完成后再执行后续操作。
3. twocalls方法中flatMap未执行的原因
这个方法存在两个问题:
- 如果
httpCall返回的响应体不是空字符串,flatMap没有返回任何Mono,会导致编译错误或运行时异常; - 如果
httpCall请求失败(比如网络错误、接口返回错误状态码),没有错误处理逻辑,会导致整个链条中断。
修复后的代码:
public Mono<UserResourceDTO> twocalls(UserResourceDTO userResourceDTO){ return httpCall(userResourceDTO) .flatMap(res -> { System.out.println(res); // 接口返回201且响应体为空 if("".equals(res)){ return userRepository.createUser(userResourceDTO); } else { // 补充其他情况的返回值 return Mono.empty(); } }) .onErrorResume(e -> { // 处理httpCall可能出现的错误 System.out.println("httpCall failed: " + e.getMessage()); return Mono.error(e); }); }
内容的提问来源于stack exchange,提问作者James

