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

Spring Boot 5 WebFlux Mono调用外部API无法获取错误信息

嘿,这个问题我之前做项目时也踩过坑!默认情况下WebClient的retrieve()方法只要收到非2xx的状态码,就会直接抛出WebClientResponseException,这时候你确实没法直接拿到API返回的错误JSON。别担心,下面给你几种实用的解决办法:

方法1:用onStatus自定义错误拦截

你可以通过onStatus方法针对特定状态码(比如所有非2xx)拦截响应,手动解析返回的错误体:

webClient.post()
    .uri("你的外部API地址")
    .bodyValue(请求体对象)
    .retrieve()
    // 拦截所有非2xx的状态码
    .onStatus(status -> !status.is2xxSuccessful(), clientResponse -> {
        // 将错误响应体解析为自定义的ErrorResponse类
        return clientResponse.bodyToMono(ErrorResponse.class)
            .flatMap(errorBody -> {
                // 把错误信息包装成自定义异常抛出,方便后续处理
                return Mono.error(new CustomApiException(
                    errorBody.getError().getMessage(),
                    errorBody.getError().getStatusCode()
                ));
            });
    })
    .bodyToMono(你的成功响应类型.class)
    .subscribe(
        successResult -> { /* 处理正常返回的业务逻辑 */ },
        error -> {
            if (error instanceof CustomApiException) {
                CustomApiException apiError = (CustomApiException) error;
                System.out.println("API错误状态码:" + apiError.getStatusCode());
                System.out.println("API错误详情:" + apiError.getMessage());
            }
        }
    );

首先你需要创建对应API错误结构的实体类:

// 完全匹配API返回的错误JSON结构
public class ErrorResponse {
    private ErrorDetail error;

    // getter、setter 省略
    public static class ErrorDetail {
        private int statusCode;
        private String name;
        private String message;

        // getter、setter 省略
    }
}

再定义一个自定义异常用来携带错误信息:

public class CustomApiException extends RuntimeException {
    private int statusCode;

    public CustomApiException(String message, int statusCode) {
        super(message);
        this.statusCode = statusCode;
    }

    // getter 省略
}
方法2:用exchangeToMono统一处理所有响应

如果想更灵活地控制成功和错误分支的逻辑,exchangeToMono会是更好的选择——它不会自动抛出异常,你可以自己判断状态码:

webClient.post()
    .uri("你的外部API地址")
    .bodyValue(请求体对象)
    .exchangeToMono(clientResponse -> {
        if (clientResponse.statusCode().is2xxSuccessful()) {
            // 状态码正常,解析成功响应体
            return clientResponse.bodyToMono(你的成功响应类型.class);
        } else {
            // 状态码异常,解析错误响应体并抛出自定义异常
            return clientResponse.bodyToMono(ErrorResponse.class)
                .flatMap(errorBody -> Mono.error(new CustomApiException(
                    errorBody.getError().getMessage(),
                    errorBody.getError().getStatusCode()
                )));
        }
    })
    .subscribe(
        successResult -> { /* 成功逻辑 */ },
        error -> {
            if (error instanceof CustomApiException) {
                CustomApiException apiError = (CustomApiException) error;
                // 这里就能拿到完整的错误详情了
                System.out.println("错误信息:" + apiError.getMessage());
            }
        }
    );
额外小技巧:从默认异常中提取错误体

如果你不想改动太多现有代码,也可以直接捕获WebClientResponseException,然后从异常中提取响应体:

webClient.post()
    .uri("你的外部API地址")
    .bodyValue(请求体对象)
    .retrieve()
    .bodyToMono(你的成功响应类型.class)
    .subscribe(
        successResult -> { /* 成功逻辑 */ },
        error -> {
            if (error instanceof WebClientResponseException) {
                WebClientResponseException ex = (WebClientResponseException) error;
                // 获取错误体字符串
                String errorBodyStr = ex.getResponseBodyAsString();
                // 用ObjectMapper转成ErrorResponse对象
                ObjectMapper mapper = new ObjectMapper();
                try {
                    ErrorResponse errorResponse = mapper.readValue(errorBodyStr, ErrorResponse.class);
                    System.out.println("API返回的错误详情:" + errorResponse.getError().getMessage());
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }
            }
        }
    );

个人更推荐前两种方法,代码结构更清晰,也方便后续统一处理异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:39:06