WebClient (推荐的HTTP客户端)

未匹配的标注

Spring WebClient 使用指南

目录

  1. WebClient 简介
  2. 快速入门
  3. 配置详解
  4. 发送请求
  5. 处理响应
  6. 高级特性
  7. 测试
  8. 总结与最佳实践

  1. WebClient 简介
    WebClient 是 Spring 5 引入的响应式非阻塞 HTTP 客户端,替代传统的 RestTemplate,专为 Spring WebFlux 设计,支持 Reactive Streams 和函数式编程。

核心特性
• 非阻塞 I/O:基于 Project Reactor(如 MonoFlux)。

• 链式 API:通过 Builder 模式配置请求。

• 灵活的数据绑定:支持 JSON、XML、表单等格式。

• 可扩展性:支持过滤器、拦截器和自定义编解码器。


  1. 快速入门

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();

  1. 配置详解

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();

  1. 发送请求

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();

  1. 处理响应

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")));

  1. 高级特性

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();

  1. 测试

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();
    }
}

  1. 总结与最佳实践

关键点

  1. 资源释放:确保订阅(subscribe)或阻塞(block)所有响应。
  2. 异常处理:使用 onStatusonErrorResume 处理 HTTP 错误。
  3. 合理配置:根据需求调整超时、连接池和重试策略。
  4. 日志与监控:添加过滤器记录请求和响应。

最佳实践
• 为不同服务创建独立的 WebClient 实例。

• 使用 @Configuration 集中管理客户端配置。

• 避免在响应式链中执行阻塞操作(如 Thread.sleep)。


通过本文档,您应能掌握 WebClient 的核心用法,并灵活应用于实际项目中。

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
唐章明
讨论数量: 0
发起讨论 查看所有版本


暂无话题~