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
相关产品推荐
相关产品推荐

