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

Spring Boot Rest Client调用下游服务时如何重抛相同异常及响应头

解决方案

核心优化思路

  • 将受检异常ResourceNotFoundException改为运行时异常,避免方法签名上冗余的throws声明
  • 为RestTemplate全局配置自定义响应错误处理器,统一处理下游服务返回的异常响应,无需每个业务方法单独写try-catch逻辑
  • 异常解析逻辑统一收口,避免重复代码,同时支持透传原响应的状态码、错误信息、响应头

具体实现步骤

1. 改造自定义异常为运行时异常

原ResourceNotFoundException继承自Exception属于受检异常,必须显式声明抛出,改为继承RuntimeException即可避免冗余的throws声明,可选新增响应头字段用于透传上游返回的头信息:

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
    
    private static final long serialVersionUID = 1L;
    // 可选:存储上游返回的响应头,用于透传
    private HttpHeaders responseHeaders;

    public ResourceNotFoundException(String message) {
        super(message);
    }

    // 可选:带响应头的构造方法
    public ResourceNotFoundException(String message, HttpHeaders responseHeaders) {
        super(message);
        this.responseHeaders = responseHeaders;
    }

    public HttpHeaders getResponseHeaders() {
        return responseHeaders;
    }
}

2. 自定义RestTemplate响应错误处理器

实现ResponseErrorHandler接口,把异常解析、抛异常的逻辑统一收口到处理器中,避免每个业务方法重复编写:

@Component
public class RestTemplateErrorHandler implements ResponseErrorHandler {

    // 注入Spring容器管理的单例ObjectMapper,避免重复创建的性能开销
    @Autowired
    private ObjectMapper objectMapper;

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

    @Override
    public void handleError(ClientHttpResponse response) throws IOException {
        String responseBody = StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8);
        ErrorDetails errorDetails = objectMapper.readValue(responseBody, ErrorDetails.class);
        // 根据状态码抛出对应自定义异常
        if (response.getStatusCode() == HttpStatus.NOT_FOUND) {
            // 可选:传入原响应头用于后续透传
            throw new ResourceNotFoundException(errorDetails.getMessage(), response.getHeaders());
        }
        // 可扩展更多状态码和自定义异常的映射逻辑,比如500对应业务系统异常
        throw new RuntimeException(errorDetails.getMessage());
    }
}

3. 注册自定义错误处理器到RestTemplate

在Spring配置类中将自定义错误处理器绑定到RestTemplate实例,后续所有通过该实例发起的请求都会自动走错误处理逻辑:

@Configuration
public class RestTemplateConfig {

    @Autowired
    private RestTemplateErrorHandler restTemplateErrorHandler;

    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setErrorHandler(restTemplateErrorHandler);
        return restTemplate;
    }
}

4.(可选)GlobalExceptionHandler适配透传响应头

如果需要把layer1返回的响应头原样返回给layer2的调用方,调整全局异常处理器的逻辑即可:

@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<?> resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
    ErrorDetails errorDetails = new ErrorDetails(new Date(),ex.getMessage(), request.getDescription(false),"ServiceLayer");
    // 透传上游返回的响应头
    return new ResponseEntity<>(errorDetails, ex.getResponseHeaders(), HttpStatus.NOT_FOUND);
}

改造后的业务代码示例

优化后业务方法完全不需要冗余的try-catch和throws声明,回到最简洁的写法:

public CompanyDto getCompanyById(Long companyId) {      
    log.info(">> Calling get company by id : {}", companyId);
    final String finalUrl = "http://localhost:8080/api/company/"+companyId;
    ResponseEntity<CompanyDto> response= restTemplate.exchange(finalUrl, HttpMethod.GET,ResourceUtil.getNewHttpEntity(null), new ParameterizedTypeReference<CompanyDto>() {});
    return response.getBody();
}

额外优化点

  • 可扩展错误处理器的状态码映射逻辑,支持更多自定义业务异常的自动转换
  • 若后续切换为OpenFeign、RestClient等其他HTTP客户端,也可采用类似全局拦截/错误处理的思路实现相同效果
  • 可以在异常中增加上游服务标识字段,方便排查跨服务调用的问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 09:45:02