WebClient 详解

在这里插入图片描述
WebClient 是 Spring WebFlux 提供的非阻塞、响应式 HTTP 客户端,用于替代传统的 RestTemplate(已过时),支持异步、流式请求处理,非常适合微服务间通信或调用外部 API。以下是其核心 API 和使用方式的详细讲解:

一、核心概念与创建方式

WebClient 基于响应式编程模型,所有操作返回 MonoFlux,支持链式调用。

1. 创建 WebClient
// 方式1:默认创建(无基础URL)

WebClient webClient = WebClient.create();

// 方式2:指定基础URL(适合调用同一服务的多个接口)

WebClient webClient = WebClient.create("https://api.example.com");

// 方式3:自定义配置(超时、拦截器等)

WebClient webClient = WebClient.builder()

      .baseUrl("https://api.example.com")

      .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) // 默认请求头

      .clientConnector(new ReactorClientHttpConnector(

          HttpClient.create()

              .responseTimeout(Duration.ofSeconds(10)) // 响应超时

      ))

      .filter((request, next) -> { // 请求拦截器(如添加Token)

          ClientRequest filtered = ClientRequest.from(request)

              .header("Authorization", "Bearer token")

              .build();

          return next.exchange(filtered);

      })

      .build();

二、核心 API 与请求流程

WebClient 的使用遵循三步流程

创建请求(method + URL)→ 配置请求(头、参数、体)→ 发送请求并处理响应

1. 定义 HTTP 方法与 URL(第一步)

通过 method(HTTP_METHOD) 或快捷方法(get()/post() 等)指定请求类型,结合 uri() 设置 URL:

// 基础用法

webClient.get() // HTTP方法:GET

       .uri("/users") // URL路径(基于baseUrl)

// 带路径参数

webClient.get()

       .uri("/users/{id}", 1L) // 路径参数:/users/1

// 带查询参数

webClient.get()

       .uri(uriBuilder -> uriBuilder

           .path("/users")

           .queryParam("name", "Alice")

           .queryParam("page", 1)

           .build()) // 结果:/users?name=Alice&page=1
2. 配置请求(第二步)

通过链式方法配置请求头、请求体、Cookie 等:

方法作用示例
header(name, value)设置单个请求头.header("Accept", "application/json")
headers(consumer)批量设置请求头.headers(headers -> headers.setBasicAuth("user", "pass"))
body(...)设置请求体(支持 Mono/Flux.body(Mono.just(user), User.class)
bodyValue(value)简化版设置请求体(直接传对象).bodyValue(user)
cookie(name, value)设置 Cookie.cookie("sessionId", "xxx")
attribute(key, val)设置自定义属性(供拦截器使用).attribute("traceId", "123")
3. 发送请求并处理响应(第三步)

通过以下方法发送请求,返回响应式结果(Mono/Flux):

方法作用适用场景
retrieve()简化的响应处理(自动提取响应体)大多数场景,直接获取响应体
exchangeToMono()完整响应处理(可获取状态码、头、体)需要处理状态码(如 404/500)或响应头
exchangeToFlux()流式响应处理(适合 SSE 等流式数据)处理服务器推送的流式响应(如 text/event-stream

三、响应处理详解

1. 简化处理:retrieve()

自动提取响应体,适合只关注数据的场景,错误状态码(如 4xx/5xx)会触发 onError

// 1. 获取单个对象(Mono)

Mono<User> userMono = webClient.get()

      .uri("/users/1")

      .retrieve()

      .bodyToMono(User.class); // 响应体转为 User 对象

// 2. 获取列表(Flux)

Flux<User> userFlux = webClient.get()

      .uri("/users")

      .retrieve()

      .bodyToFlux(User.class); // 响应体转为 User 流

// 3. 处理错误状态码

Mono<User> userWithErrorHandler = webClient.get()

      .uri("/users/999")

      .retrieve()

      .onStatus(HttpStatus::is4xxClientError, response -> { // 处理 4xx 错误

          return Mono.error(new RuntimeException("用户不存在"));

      })

      .onStatus(HttpStatus::is5xxServerError, response -> { // 处理 5xx 错误

          return Mono.error(new RuntimeException("服务器错误"));

      })

      .bodyToMono(User.class);
2. 完整处理:exchangeToMono()/exchangeToFlux()

获取完整的 ClientResponse(包含状态码、响应头、响应体),适合复杂场景:

// 处理完整响应(状态码 + 响应体)

Mono<Result<User>> resultMono = webClient.get()

      .uri("/users/1")

      .exchangeToMono(response -> {

          // 获取状态码

          HttpStatus status = response.statusCode();

          // 根据状态码处理

          if (status.is2xxSuccessful()) {

              // 成功:解析响应体

              return response.bodyToMono(User.class)

                      .map(user -> new Result<>(true, user, null));

          } else {

              // 失败:返回错误信息

              return response.bodyToMono(String.class)

                      .map(error -> new Result<>(false, null, error));

          }

      });
3. 处理流式响应(如 SSE)

对于服务器推送的流式数据(text/event-stream),用 exchangeToFlux()retrieve() 配合 bodyToFlux()

// 接收 SSE 流式数据

Flux<Event> eventFlux = webClient.get()

      .uri("/stream/events")

      .accept(MediaType.TEXT_EVENT_STREAM) // 声明接收流式数据

      .retrieve()

      .bodyToFlux(Event.class); // 逐个解析流中的事件

// 订阅并处理每个事件

eventFlux.subscribe(

  event -> System.out.println("收到事件:" + event),

  error -> System.err.println("错误:" + error),

  () -> System.out.println("流结束")

);

四、常用场景示例

1. POST 请求(提交数据)
// 提交单个对象并获取响应

Mono<User> createdUser = webClient.post()

      .uri("/users")

      .contentType(MediaType.APPLICATION_JSON) // 设置请求体类型

      .bodyValue(new User(null, "Bob")) // 请求体:User对象(自动转为JSON)

      .retrieve()

      .bodyToMono(User.class); // 响应体:创建的User(含ID)
2. PUT 请求(更新数据)
Mono<Void> updateResult = webClient.put()

      .uri("/users/{id}", 1L)

      .bodyValue(new User(1L, "Alice Updated"))

      .retrieve()

      .bodyToMono(Void.class); // 无响应体,用 Void
3. DELETE 请求
Mono<Void> deleteResult = webClient.delete()

      .uri("/users/{id}", 1L)

      .retrieve()

      .bodyToMono(Void.class);

五、高级特性

1. 超时设置

通过 HttpClient 配置全局超时,或为单个请求设置超时:

// 单个请求超时

Mono<User> userWithTimeout = webClient.get()

      .uri("/users/1")

      .httpRequest(httpRequest -> {

          httpRequest.timeout(Duration.ofSeconds(5)); // 该请求超时5秒

      })

      .retrieve()

      .bodyToMono(User.class);
2. 文件上传 / 下载
// 下载文件(保存到本地)

webClient.get()

      .uri("/files/report.pdf")

      .retrieve()

      .bodyToFlux(DataBuffer.class) // 以数据流接收文件

      .map(dataBuffer -> {

          // 写入本地文件

          Files.write(Paths.get("report.pdf"), DataBufferUtils.extract(dataBuffer));

          DataBufferUtils.release(dataBuffer); // 释放资源

          return true;

      })

      .blockLast(); // 阻塞等待完成(仅示例,实际应避免阻塞)

六、与 RestTemplate 对比

特性WebClient(WebFlux)RestTemplate(Spring MVC)
编程模型响应式(异步非阻塞)命令式(同步阻塞)
性能高(适合高并发、IO 密集场景)一般(阻塞 IO,线程开销大)
流式处理支持(Flux 处理数据流)不支持(需一次性加载全部数据)
背压支持支持(根据下游处理能力调节上游速度)不支持
扩展性好(可集成响应式生态如 Kafka有限

总结

WebClient 是 Spring 响应式生态中的核心 HTTP 客户端,其 API 设计遵循链式调用,通过 创建请求→配置请求→处理响应 三步流程完成操作。关键是根据场景选择 retrieve()(简化)或 exchangeToMono()(完整控制),并利用 Mono/Flux 处理异步结果。相比传统的 RestTemplate,它更适合现代微服务架构中的高并发场景。

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐