WebClient (推荐的HTTP客户端)
Spring WebClient 使用指南
目录
- WebClient 简介
- 快速入门
- 配置详解
- 发送请求
- 处理响应
- 高级特性
- 测试
- 总结与最佳实践
- WebClient 简介
WebClient 是 Spring 5 引入的响应式非阻塞 HTTP 客户端,替代传统的RestTemplate
,专为 Spring WebFlux 设计,支持 Reactive Streams 和函数式编程。
核心特性
• 非阻塞 I/O:基于 Project Reactor(如 Mono
和 Flux
)。
• 链式 API:通过 Builder 模式配置请求。
• 灵活的数据绑定:支持 JSON、XML、表单等格式。
• 可扩展性:支持过滤器、拦截器和自定义编解码器。
- 快速入门
2.1 添加依赖
xml
复制
<!-- Spring Boot WebFlux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
2.2 创建 WebClient 实例
java
复制
// 默认配置
WebClient client = WebClient.create();
// 指定 baseUrl
WebClient client = WebClient.builder()
.baseUrl("https://api.example.com")
.defaultHeader("User-Agent", "MyApp")
.build();
- 配置详解
3.1 基础配置
通过 WebClient.Builder
配置:
java
复制
WebClient client = WebClient.builder()
.baseUrl("https://api.example.com")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultCookie("auth", "token123")
.build();
3.2 自定义配置
连接超时与 SSL
java
复制
HttpClient httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
.sslContext(SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE));
WebClient client = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
拦截器(ExchangeFilterFunction)
java
复制
WebClient client = WebClient.builder()
.filter((request, next) -> {
System.out.println("Request: " + request.method() + " " + request.url());
return next.exchange(request);
})
.build();
- 发送请求
4.1 GET 请求
java
复制
Mono<User> userMono = client.get()
.uri("/users/{id}", 1)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(User.class);
// 异步执行
userMono.subscribe(user -> System.out.println(user));
4.2 POST 请求
JSON 数据
java
复制
User newUser = new User("Alice", "alice@example.com");
Mono<User> createdUser = client.post()
.uri("/users")
.bodyValue(newUser)
.retrieve()
.bodyToMono(User.class);
表单数据
java
复制
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add("name", "Bob");
formData.add("email", "bob@example.com");
Mono<Void> result = client.post()
.uri("/users/form")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromFormData(formData))
.retrieve()
.bodyToMono(Void.class);
4.3 PUT 请求
java
复制
client.put()
.uri("/users/{id}", 1)
.bodyValue(updatedUser)
.retrieve()
.toBodilessEntity()
.subscribe(response -> System.out.println("Updated"));
4.4 DELETE 请求
java
复制
client.delete()
.uri("/users/{id}", 1)
.retrieve()
.toBodilessEntity()
.subscribe(response -> System.out.println("Deleted"));
4.5 文件上传
java
复制
MultipartBodyBuilder builder = new MultipartBodyBuilder();
builder.part("file", new FileSystemResource("test.txt"));
builder.part("metadata", "{\"type\":\"text\"}");
client.post()
.uri("/upload")
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(BodyInserters.fromMultipartData(builder.build()))
.retrieve()
.bodyToMono(String.class)
.subscribe();
- 处理响应
5.1 处理状态码
java
复制
client.get()
.uri("/users/{id}", 99)
.retrieve()
.onStatus(status -> status == HttpStatus.NOT_FOUND, response ->
Mono.error(new UserNotFoundException()))
.bodyToMono(User.class);
5.2 提取数据
java
复制
// 提取为对象
Mono<User> user = response.bodyToMono(User.class);
// 提取为集合
Flux<User> users = response.bodyToFlux(User.class);
// 提取为原始数据
Mono<String> text = response.bodyToMono(String.class);
5.3 错误处理
java
复制
client.get()
.uri("/error-endpoint")
.retrieve()
.bodyToMono(String.class)
.onErrorResume(WebClientResponseException.class, ex -> {
System.out.println("Error: " + ex.getStatusCode());
return Mono.empty();
});
5.4 流式响应
java
复制
Flux<String> stream = client.get()
.uri("/stream")
.accept(MediaType.TEXT_EVENT_STREAM)
.retrieve()
.bodyToFlux(String.class);
stream.subscribe(data -> System.out.println("Received: " + data));
5.5 超时配置
java
复制
Mono<User> user = client.get()
.uri("/users/{id}", 1)
.retrieve()
.bodyToMono(User.class)
.timeout(Duration.ofSeconds(3))
.onErrorResume(TimeoutException.class, ex -> Mono.just(new User("fallback")));
- 高级特性
6.1 连接池管理
java
复制
HttpClient httpClient = HttpClient.create()
.connectionProvider(
ConnectionProvider.builder("custom")
.maxConnections(50)
.pendingAcquireTimeout(Duration.ofSeconds(10))
.build()
);
WebClient client = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
6.2 自定义 SSL
java
复制
SslContext sslContext = SslContextBuilder.forClient()
.trustManager(new File("truststore.pem"))
.keyManager(new File("cert.pem"), new File("key.pem"))
.build();
HttpClient httpClient = HttpClient.create().secure(ssl -> ssl.sslContext(sslContext));
6.3 重试机制
java
复制
Mono<User> user = client.get()
.uri("/users/{id}", 1)
.retrieve()
.bodyToMono(User.class)
.retryWhen(Retry.backoff(3, Duration.ofSeconds(1)));
6.4 请求日志
java
复制
client.mutate()
.filters(filters -> filters.add((request, next) -> {
System.out.println("Request: " + request.method() + " " + request.url());
return next.exchange(request);
}))
.build();
- 测试
7.1 使用 MockWebServer
java
复制
@SpringBootTest
public class WebClientTest {
private MockWebServer mockServer;
private WebClient client;
@BeforeEach
void setup() {
mockServer = new MockWebServer();
client = WebClient.create(mockServer.url("/").toString());
}
@Test
void testGetUser() {
mockServer.enqueue(new MockResponse()
.setResponseCode(200)
.setHeader("Content-Type", "application/json")
.setBody("{\"name\":\"Alice\"}"));
Mono<User> userMono = client.get().uri("/users/1").retrieve().bodyToMono(User.class);
StepVerifier.create(userMono)
.expectNextMatches(user -> user.getName().equals("Alice"))
.verifyComplete();
}
}
- 总结与最佳实践
关键点
- 资源释放:确保订阅(
subscribe
)或阻塞(block
)所有响应。 - 异常处理:使用
onStatus
和onErrorResume
处理 HTTP 错误。 - 合理配置:根据需求调整超时、连接池和重试策略。
- 日志与监控:添加过滤器记录请求和响应。
最佳实践
• 为不同服务创建独立的 WebClient
实例。
• 使用 @Configuration
集中管理客户端配置。
• 避免在响应式链中执行阻塞操作(如 Thread.sleep
)。
通过本文档,您应能掌握 WebClient 的核心用法,并灵活应用于实际项目中。