当前位置: 首页 > news >正文

Spring Boot项目使用WebClient调用第三方接口详细教程

目录

一、简单说明

步骤1: 添加依赖

步骤2: 创建WebClient实例

步骤3: 构建HTTP请求

步骤4: 处理响应

步骤5: 完整示例代码

常见问题和最佳实践

总结

二、抽象化设计

一、全局WebClient配置

二、通用API Service封装

三、业务Service实现

四、Controller调用示例

五、定时任务调用示例

六、关键设计说明


在Spring Boot项目中,WebClient 是Spring WebFlux模块提供的非阻塞式HTTP客户端,用于高效地调用RESTful API。它支持响应式编程,性能优于传统的RestTemplate。本教程将逐步指导您从零开始集成和使用WebClient,包括依赖配置、实例创建、请求构建、响应处理和完整代码示例。确保您使用Spring Boot 2.x或更高版本(推荐Spring Boot 3.x)。

一、简单说明

步骤1: 添加依赖

首先,在您的Spring Boot项目中添加必要的依赖。WebClient 需要spring-boot-starter-webflux模块,它包含响应式核心库。打开pom.xml文件,添加以下依赖:

<dependencies><!-- Spring WebFlux for WebClient --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency><!-- 可选:用于JSON处理,如Jackson --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency>
</dependencies>

如果您使用Gradle,在build.gradle中添加:

dependencies {implementation 'org.springframework.boot:spring-boot-starter-webflux'implementation 'com.fasterxml.jackson.core:jackson-databind'
}

步骤2: 创建WebClient实例

WebClient 可以通过Spring Bean方式全局配置,或直接在代码中创建。推荐使用Bean方式以便重用和统一配置。

  • 方式1: 通过Bean全局配置(推荐) 在Spring配置类中定义WebClient Bean。例如,创建一个WebClientConfig类:

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.reactive.function.client.WebClient;@Configuration
    public class WebClientConfig {@Beanpublic WebClient webClient() {return WebClient.builder().baseUrl("https://api.example.com") // 设置基础URL,可选.defaultHeader("Content-Type", "application/json") // 默认请求头.build();}
    }
    

  • 方式2: 直接创建实例 在需要的地方直接构建WebClient

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

步骤3: 构建HTTP请求

WebClient 支持GET、POST、PUT、DELETE等方法。使用链式调用来设置URL、头信息、查询参数和请求体。

  • GET请求示例:调用一个第三方API获取数据。

    import org.springframework.web.reactive.function.client.WebClient;
    import reactor.core.publisher.Mono;public class ApiClient {private final WebClient webClient;public ApiClient(WebClient webClient) {this.webClient = webClient;}public Mono<String> getData(String apiPath) {return webClient.get().uri(apiPath) // 例如: "/users".header("Authorization", "Bearer your_token") // 添加认证头.retrieve() // 发送请求.bodyToMono(String.class); // 将响应体解析为String}
    }
    

  • POST请求示例:发送JSON数据到第三方API。

    import org.springframework.web.reactive.function.client.WebClient;
    import reactor.core.publisher.Mono;public class ApiClient {public Mono<String> postData(String apiPath, Object requestBody) {return webClient.post().uri(apiPath).header("Content-Type", "application/json").bodyValue(requestBody) // 设置请求体,自动序列化为JSON.retrieve().bodyToMono(String.class);}
    }
    

步骤4: 处理响应

WebClient 返回MonoFlux对象,您需要订阅来处理响应。响应处理包括错误处理和反序列化。

  • 基本响应处理:使用block()同步获取结果(不推荐在生产中用,测试可用),或异步处理。

    // 在Service类中调用
    public class UserService {private final ApiClient apiClient;public UserService(ApiClient apiClient) {this.apiClient = apiClient;}public String fetchUserData() {Mono<String> response = apiClient.getData("/users/1");return response.block(); // 同步获取,仅用于演示}
    }
    

  • 异步处理(推荐):在Controller中使用响应式风格。

    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import reactor.core.publisher.Mono;@RestController
    public class UserController {private final ApiClient apiClient;public UserController(ApiClient apiClient) {this.apiClient = apiClient;}@GetMapping("/user")public Mono<String> getUser() {return apiClient.getData("/users/1").onErrorResume(e -> Mono.just("Error: " + e.getMessage())); // 错误处理}
    }
    

  • 反序列化为对象:如果第三方API返回JSON,使用Jackson自动映射到Java对象。

    public class User {private Long id;private String name;// Getters and setters
    }// 在ApiClient中
    public Mono<User> getUserObject(String apiPath) {return webClient.get().uri(apiPath).retrieve().bodyToMono(User.class); // 直接映射到User对象
    }
    

步骤5: 完整示例代码

以下是一个完整的Spring Boot应用示例,包括配置、服务和控制器。

  1. 配置类 (WebClientConfig.java):

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.reactive.function.client.WebClient;@Configuration
    public class WebClientConfig {@Beanpublic WebClient webClient() {return WebClient.builder().baseUrl("https://jsonplaceholder.typicode.com") // 免费测试API.build();}
    }
    

  2. API客户端 (ApiClient.java):

    import org.springframework.stereotype.Component;
    import org.springframework.web.reactive.function.client.WebClient;
    import reactor.core.publisher.Mono;@Component
    public class ApiClient {private final WebClient webClient;public ApiClient(WebClient webClient) {this.webClient = webClient;}public Mono<String> getPosts() {return webClient.get().uri("/posts").retrieve().bodyToMono(String.class);}
    }
    

  3. 控制器 (UserController.java):

    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import reactor.core.publisher.Mono;@RestController
    public class UserController {private final ApiClient apiClient;public UserController(ApiClient apiClient) {this.apiClient = apiClient;}@GetMapping("/posts")public Mono<String> getPosts() {return apiClient.getPosts();}
    }
    

  4. 主应用 (SpringBootApplication.java):

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
    public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
    }
    

常见问题和最佳实践
  • 错误处理:使用onErrorResumeonStatus处理HTTP错误(例如404或500)。

    return webClient.get().uri("/invalid-path").retrieve().onStatus(status -> status.is4xxClientError(), response -> Mono.error(new RuntimeException("Client error"))).bodyToMono(String.class);
    

  • 超时配置:设置请求超时,避免阻塞。

    WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create().responseTimeout(Duration.ofSeconds(10)))).build();
    

  • 性能优化WebClient是非阻塞的,适合高并发场景。确保整个调用链使用响应式编程(如返回Mono/Flux)。

  • 测试:使用WebTestClient进行单元测试,模拟第三方API。

总结

通过本教程,您学会了在Spring Boot中使用WebClient调用第三方接口:

  1. 添加spring-boot-starter-webflux依赖。
  2. 配置WebClient Bean。
  3. 构建GET/POST请求并处理响应。
  4. 使用异步处理和错误机制。
  5. 完整代码示例可直接运行。

WebClient 的优势包括高吞吐量、低资源消耗和现代化API设计。如果您遇到问题,请参考Spring官方文档。在实际项目中,确保添加日志和监控以跟踪请求。

二、抽象化设计

以下是根据需求设计的Spring Boot项目结构,包含通用API Service封装、业务Service及调用示例:

一、全局WebClient配置

@Configuration
public class WebClientConfig {@Beanpublic WebClient webClient() {return WebClient.builder().filter(logRequest()) // 请求日志过滤器.filter(logResponse()) // 响应日志过滤器.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).build();}private ExchangeFilterFunction logRequest() {return (request, next) -> {log.info("Request: {} {}", request.method(), request.url());request.headers().forEach((name, values) -> values.forEach(value -> log.info("Header: {}={}", name, value)));return next.exchange(request);};}private ExchangeFilterFunction logResponse() {return ExchangeFilterFunction.ofResponseProcessor(response -> {log.info("Response status: {}", response.statusCode());return Mono.just(response);});}
}

二、通用API Service封装

@Service
@Slf4j
public class ApiService {private final WebClient webClient;public ApiService(WebClient webClient) {this.webClient = webClient;}// 同步调用public <T, R> R callSync(String url, HttpMethod method, @Nullable T requestBody, Class<R> responseType, MultiValueMap<String, String> headers) {log.info("Request body: {}", toJson(requestBody));try {WebClient.RequestBodySpec requestSpec = webClient.method(method).uri(url).headers(h -> h.addAll(headers));if (requestBody != null) {requestSpec.bodyValue(requestBody);}return requestSpec.retrieve().onStatus(HttpStatusCode::isError, this::handleError).bodyToMono(responseType).doOnNext(res -> log.info("Response body: {}", toJson(res))).block();} catch (WebClientResponseException e) {log.error("API call failed: status={}, body={}", e.getStatusCode(), e.getResponseBodyAsString());throw new ApiException("API调用失败", e);} catch (Exception e) {log.error("Unexpected error", e);throw new ApiException("系统异常", e);}}// 异步调用public <T, R> Mono<R> callAsync(String url, HttpMethod method, @Nullable T requestBody, Class<R> responseType, MultiValueMap<String, String> headers) {log.info("Async request body: {}", toJson(requestBody));WebClient.RequestBodySpec requestSpec = webClient.method(method).uri(url).headers(h -> h.addAll(headers));if (requestBody != null) {requestSpec.bodyValue(requestBody);}return requestSpec.retrieve().onStatus(HttpStatusCode::isError, this::handleError).bodyToMono(responseType).doOnNext(res -> log.info("Async response body: {}", toJson(res))).onErrorResume(WebClientResponseException.class, ex -> {log.error("Async API error: status={}, body={}", ex.getStatusCode(), ex.getResponseBodyAsString());return Mono.error(new ApiException("异步调用失败", ex));});}private Mono<Throwable> handleError(ClientResponse response) {return response.bodyToMono(String.class).flatMap(body -> {log.error("Error response: status={}, body={}", response.statusCode(), body);return Mono.error(new WebClientResponseException(response.statusCode().value(),"API Error",response.headers().asHttpHeaders(),body.getBytes(),StandardCharsets.UTF_8));});}private String toJson(Object obj) {try {return new ObjectMapper().writeValueAsString(obj);} catch (JsonProcessingException e) {log.warn("JSON serialization error", e);return "{}";}}
}// 自定义异常
public class ApiException extends RuntimeException {public ApiException(String message, Throwable cause) {super(message, cause);}
}

三、业务Service实现

@Service
@Slf4j
public class BusinessService {private final ApiService apiService;// 业务请求体封装@Data@AllArgsConstructor@NoArgsConstructorpublic static class BusinessRequest {private String orderId;private Integer amount;}// 业务响应体封装@Datapublic static class BusinessResponse {private String transactionId;private LocalDateTime processTime;}public BusinessResponse executeBusinessLogic(String param) {// 1. 构建请求参数BusinessRequest request = new BusinessRequest(param, 100);HttpHeaders headers = new HttpHeaders();headers.add("X-Client-ID", "business-service");// 2. 调用APIreturn apiService.callSync("https://api.thirdparty.com/endpoint",HttpMethod.POST,request,BusinessResponse.class,headers);}public Mono<BusinessResponse> executeAsyncBusiness(String param) {BusinessRequest request = new BusinessRequest(param, 200);HttpHeaders headers = new HttpHeaders();headers.add("X-Async", "true");return apiService.callAsync("https://api.thirdparty.com/async",HttpMethod.POST,request,BusinessResponse.class,headers);}
}

四、Controller调用示例

@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class BusinessController {private final BusinessService businessService;@PostMapping("/process")public ResponseEntity<BusinessService.BusinessResponse> processOrder(@RequestParam String orderId) {return ResponseEntity.ok(businessService.executeBusinessLogic(orderId));}
}

五、定时任务调用示例

@Component
@Slf4j
@RequiredArgsConstructor
public class ScheduledTask {private final BusinessService businessService;@Scheduled(fixedRate = 30000)public void runBatchJob() {businessService.executeAsyncBusiness("BATCH_001").subscribe(response -> log.info("Batch processed: {}", response.getTransactionId()),error -> log.error("Batch failed", error));}
}

六、关键设计说明

  1. 日志记录

    • 使用SLF4J的@Slf4j注解
    • 记录原始请求/响应JSON(通过Jackson序列化)
    • 异常场景单独记录错误日志
  2. 异常处理

    • 自定义ApiException统一封装异常
    • 区分HTTP错误状态码(4xx/5xx)
    • 处理网络超时等底层异常
  3. 类型安全

    • 请求/响应使用泛型参数化
    • 业务层DTO对象封装数据
    • 支持任意复杂对象自动序列化
  4. 全局配置

    • WebClient统一配置超时/编码/拦截器
    • 通过过滤器实现全局日志
    • 默认JSON内容类型
  5. 调用方式

    • 同步调用:直接返回结果对象
    • 异步调用:返回Mono<T>响应式流
    • 支持GET/POST等HTTP方法

使用示例:业务Service只需关注自身DTO定义,调用时传入URL、方法类型、请求对象和响应类型即可完成三方接口调用,日志和异常处理由底层自动完成。

http://www.dtcms.com/a/326950.html

相关文章:

  • Fiddler国内中文网使用经验分享,从抓包入门到API调试进阶
  • 知名B2B订货系统推荐,核货宝、数商云、易订货小程序订货系统深度评测
  • 基于开源AI大模型AI智能名片S2B2C商城小程序的母婴用品精准营销策略研究
  • 小程序打通美团核销:解锁到店综合业态私域密码,赋能6000+门店破局增长
  • 小程序排名优化:功能迭代如何助力排名攀升
  • 校园快递小程序(腾讯地图API、二维码识别、Echarts图形化分析)
  • 棋牌室|台球|KTV|亲子|游戏|PS5等小程序接入美团核销教程,解决线下门店线上的流量!
  • windows X86_64(amd64)平台 Docker 构建多环境平台报错
  • Json 中国全部省级、城市,数据来源于腾讯位置、城市选择器
  • 【KO】 Android基础
  • 【昇腾】关于Atlas 200I A2加速模块macro0配置3路PCIE+1路SATA在hboot2中的一个bug_20250812
  • 【Android】【bug】Json解析错误Expected BEGIN_OBJECT but was STRING...
  • 计算机视觉(8)-纯视觉方案实现端到端轨迹规划(模型训练+代码)
  • 虚拟机一站式部署Claude Code 可视化UI界面
  • 401 Unauthorized(未授权)​​ 和 ​​403 Forbidden(禁止访问)区别
  • python --- 基础语法(1)
  • 《飞算Java AI:从安装到需求转实战项目详细教学》
  • 论文阅读:Agricultural machinery automatic navigation technology
  • Linux文件I/O操作全解析
  • 论文阅读(九)Locality-Aware Zero-Shot Human-Object Interaction Detection
  • window 右键菜单添加 vscode
  • PySpark性能优化与多语言选型讨论
  • 【论文阅读】从表面肌电信号中提取神经信息用于上肢假肢控制:新兴途径与挑战
  • 基于跨平台的svg组件编写一个svg编辑器
  • 【论文阅读】一种基于经典机器学习的肌电下肢意图检测方法,用于人机交互系统
  • Spark Core 3.3.2 略讲~
  • Elasticsearch JavaScript 客户端「基础配置」全指南(Node/TS)
  • 人工智能+虚拟仿真,助推医学检查技术理论与实践结合
  • 运维的一些指令
  • LINUX812 shell脚本:if else,for 判断素数,创建用户