Spring WebClient未发起请求问题(Java 1.8+Spring Boot 2.7.4)
问题分析与解决方案
核心原因:响应式流的惰性求值
WebClient基于Reactor框架,返回的Mono<String>是惰性求值的——只有调用订阅方法(比如subscribe()、block())时,才会实际发起HTTP请求。你的代码仅创建了Mono对象但未触发订阅,所以请求根本没执行,自然看不到代理日志和证书错误。
修复步骤
1. 触发请求订阅
在创建Mono<String> response后,添加订阅操作:
- 同步场景(比如main方法)用
block()阻塞等待结果:
String result = response.block(); System.out.println("返回结果: " + result);
- 异步场景用
subscribe()处理结果/异常:
response.subscribe( result -> System.out.println("返回结果: " + result), error -> System.err.println("请求失败: " + error.getMessage()) );
注意:main方法执行完会立刻退出,用异步订阅时要加延迟让线程等待请求完成:
try { Thread.sleep(5000); // 等待5秒确保请求执行完毕 } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
2. 修复JSON字符串拼接问题
你代码里的JSON字符串拼接存在语法错误(换行符未转义),会导致编译失败或无效JSON。推荐两种更安全的方式:
- 手动写转义后的JSON:
BodyInserters.fromValue("{\"email\": \"test\", \"password\": \"test\"}")
- 用Jackson序列化对象(更规范,避免拼写错误):
先定义DTO类:
public class LoginRequest { private String email; private String password; // 构造器、getter/setter public LoginRequest(String email, String password) { this.email = email; this.password = password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
再传入对象序列化:
bodySpec.body(BodyInserters.fromValue(new LoginRequest("test", "test")))
Spring Boot默认集成Jackson,会自动将对象转为JSON。
3. 证书问题处理(请求发起后可能遇到)
当请求实际执行后,你大概率会遇到证书验证错误(目标是测试域名)。测试环境可临时禁用证书验证(生产环境绝对禁止):
// 创建信任所有证书的SslContext SslContext sslContext = SslContextBuilder .forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .build(); // 配置HttpClient使用自定义SslContext HttpClient httpClient = HttpClient.create() .secure(t -> t.sslContext(sslContext)); // 构建WebClient WebClient client = WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) .baseUrl("https://test.caido.ro") .build();
Spring Boot 2.7.x默认已包含io.projectreactor.netty:reactor-netty依赖,无需额外引入。
完整修复后的代码示例
@SpringBootApplication public class RestApplication { public static void main(String[] args) { System.out.println("开始构建请求"); // 测试环境跳过证书验证 SslContext sslContext = SslContextBuilder .forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .build(); HttpClient httpClient = HttpClient.create().secure(t -> t.sslContext(sslContext)); WebClient client = WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) .baseUrl("https://test.caido.ro") .build(); RequestHeadersSpec<?> headersSpec = client.post() .uri("/api/login") .body(BodyInserters.fromValue(new LoginRequest("test", "test"))); System.out.println("开始发送请求"); // 同步阻塞获取结果 try { String response = headersSpec.retrieve().bodyToMono(String.class).block(); System.out.println("返回结果: " + response); } catch (Exception e) { System.err.println("请求失败: " + e.getMessage()); } System.out.println("请求完成"); } static class LoginRequest { private String email; private String password; public LoginRequest(String email, String password) { this.email = email; this.password = password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } } }
内容的提问来源于stack exchange,提问作者Victorqedu
相关产品推荐
相关产品推荐

