java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot用WebClient调用第三方接口

Spring Boot项目使用WebClient调用第三方接口的详细教程(附实例代码)

作者:布朗克168

WebClient是Spring WebFlux模块提供的一个非阻塞的基于响应式编程的进行Http请求的客户端工具,从Spring5.0开始提供,这篇文章主要介绍了Spring Boot项目使用WebClient调用第三方接口的相关资料,需要的朋友可以参考下

引言

在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方式以便重用和统一配置。

步骤3: 构建HTTP请求

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

步骤4: 处理响应

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

步骤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 {
        @Bean
        public 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);
        }
    }
    

常见问题和最佳实践

总结

通过本教程,您学会了在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 {

    @Bean
    public 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
    @NoArgsConstructor
    public static class BusinessRequest {
        private String orderId;
        private Integer amount;
    }

    // 业务响应体封装
    @Data
    public 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. 调用API
        return 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、方法类型、请求对象和响应类型即可完成三方接口调用,日志和异常处理由底层自动完成。

总结 

到此这篇关于Spring Boot项目使用WebClient调用第三方接口的文章就介绍到这了,更多相关SpringBoot用WebClient调用第三方接口内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文