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

RestTemplate处理HttpClientErrorException如何提取目标错误消息

问题根因

当前代码捕获HttpClientErrorException后直接调用异常自带的getMessage()方法构造新异常抛出,该方法默认会拼接状态码+完整下游响应体内容,而下游返回的响应体本身包含时间戳、路径等多余字段,最终就导致外层返回的消息包含冗余内容。

可行解决方案

方案1:当前业务代码直接解析响应体

适合仅这一处接口有提取核心错误消息需求的场景,直接在现有catch块中解析错误响应体的message字段即可,示例如下(以Jackson解析为例):

// 项目中一般已自动注册ObjectMapper,直接注入即可
@Autowired
private ObjectMapper objectMapper;

public void validateOtps (ValidateOtpRequestDto requestDto, String otpId){
        try{
            HttpEntity<ValidateOtpRequestDto> requestEntity = new HttpEntity<>(requestDto);
            restTemplate.exchange(authServiceUrl + "/authentication-service/otps/" + otpId, HttpMethod.POST, requestEntity, String.class);
        }catch (HttpClientErrorException e){
            log.error("OTP校验请求失败,响应内容:{}", e.getResponseBodyAsString());
            String errorMsg = "OTP校验失败";
            try {
                // 解析下游返回的错误JSON,提取核心message
                JsonNode errorNode = objectMapper.readTree(e.getResponseBodyAsString());
                if (errorNode.has("message")) {
                    errorMsg = errorNode.get("message").asText();
                }
            } catch (JsonProcessingException parseEx) {
                log.error("解析错误响应失败", parseEx);
            }
            // 用提取到的核心消息构造异常抛出
            throw new HttpClientErrorException(e.getStatusCode(), errorMsg);
        } catch (Exception e){
            log.error("OTP校验异常", e);
        }
    }

方案2:自定义RestTemplate错误处理器统一处理

适合多处使用RestTemplate调用同一外部服务的场景,一次配置全局生效,无需每个接口重复写解析逻辑:

  1. 首先自定义错误处理器实现ResponseErrorHandler接口:
public class AuthServiceErrorHandler implements ResponseErrorHandler {
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {
        // 判断是否为错误响应
        return response.getStatusCode().isError();
    }

    @Override
    public void handleError(ClientHttpResponse response) throws IOException {
        String responseBody = StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8);
        String errorMsg = "服务调用失败";
        try {
            JsonNode errorNode = objectMapper.readTree(responseBody);
            if (errorNode.has("message")) {
                errorMsg = errorNode.get("message").asText();
            }
        } catch (Exception e) {
            // 解析失败时使用默认错误消息即可
        }
        throw new HttpClientErrorException(response.getStatusCode(), errorMsg);
    }
}
  1. 配置RestTemplate使用自定义错误处理器:
@Bean
public RestTemplate authServiceRestTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.setErrorHandler(new AuthServiceErrorHandler());
    return restTemplate;
}

后续所有调用认证服务的逻辑都注入这个自定义配置的RestTemplate即可,所有错误都会自动提取核心消息。

方案3:全局异常处理器统一处理

如果项目已经使用@RestControllerAdvice做全局异常拦截,也可以在全局层统一处理HttpClientErrorException,统一转换返回格式:

@RestControllerAdvice
public class GlobalExceptionHandler {
    private final ObjectMapper objectMapper = new ObjectMapper();

    @ExceptionHandler(HttpClientErrorException.class)
    public ResponseEntity<Map<String, Object>> handleHttpCallError(HttpClientErrorException e) {
        Map<String, Object> result = new HashMap<>();
        result.put("status", e.getStatusCode().value());
        result.put("error", null);
        
        String errorMsg = "请求失败";
        try {
            JsonNode errorNode = objectMapper.readTree(e.getResponseBodyAsString());
            if (errorNode.has("message")) {
                errorMsg = errorNode.get("message").asText();
            }
        } catch (Exception parseEx) {
            // 解析失败使用默认消息
        }
        result.put("message", errorMsg);
        return new ResponseEntity<>(result, e.getStatusCode());
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 10:54:04