You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring WebFlux中WebClient调用捕获服务端特定异常问题

问题解答

核心原因

服务B抛出的DatabaseFetchException是Java进程内异常,无法直接通过HTTP调用跨进程传递给服务A。当服务B抛出该异常时,Spring MVC会自动将其转换为HTTP错误响应(默认是500状态码),服务A的WebClient收到这个HTTP错误后,会抛出自身的异常(比如WebClientResponseException),而非服务B的DatabaseFetchException,所以你写的catch(DatabaseFetchException e)捕获块根本不会触发,最终进入通用Exception捕获块。

解决方案

要让服务A能识别并捕获服务B的自定义异常,需要分两步处理:

1. 服务B:将自定义异常转换为带标识的HTTP响应

通过全局异常处理器,把DatabaseFetchException转换成包含异常标识的HTTP响应,让服务A能识别:

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(DatabaseFetchException.class)
    public ResponseEntity<String> handleDatabaseFetchException(DatabaseFetchException ex) {
        // 返回500状态码,响应体携带异常类型标识和消息
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body("DatabaseFetchException:" + ex.getMessage());
    }
}

2. 服务A:处理HTTP错误并手动抛出目标异常

修改WebClient的调用逻辑,在收到服务B的错误响应时,解析响应体中的标识,手动抛出DatabaseFetchException,这样就能被对应的捕获块捕获:

public <T> T callExternalApi(String uri, Class<T> responseType) throws DatabaseFetchException {
    try {
        return webClient.get()
                .uri(uri)
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .retrieve()
                // 拦截服务端5xx错误响应
                .onStatus(HttpStatus::is5xxServerError, clientResponse -> {
                    return clientResponse.bodyToMono(String.class)
                            .flatMap(responseBody -> {
                                // 根据响应体中的标识判断是否为目标异常
                                if (responseBody != null && responseBody.startsWith("DatabaseFetchException:")) {
                                    String errorMsg = responseBody.substring("DatabaseFetchException:".length());
                                    return Mono.error(new DatabaseFetchException(errorMsg));
                                }
                                // 其他服务端错误抛出通用异常
                                return Mono.error(new RuntimeException("Server error: " + responseBody));
                            });
                })
                .bodyToMono(responseType)
                .block();
    } catch (DatabaseFetchException e) {
        System.out.println("Database Exception occurred");
        throw e; // 可根据业务需求选择重新抛出或处理后返回
    } catch (Exception e) {
        System.out.println("Generic Exception occurred");
        return null;
    }
}

补充说明

如果需要更规范的异常传递,也可以定义统一的错误响应DTO(比如包含errorCode、errorMessage字段),服务B返回这个DTO,服务A解析后根据errorCode判断是否抛出DatabaseFetchException,这样比字符串匹配更可靠。

内容的提问来源于stack exchange,提问作者Arunabha Ghosh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 21:50:09