Spring Boot服务间REST调用异常信息无法传递,如何获取原错误消息?
这个问题我之前也碰到过,核心原因其实是被调用服务没有把异常消息正确传递到HTTP响应里,而调用方又没从响应体里提取真正的错误内容,才拿到了那种笼统的I/O错误提示。咱们一步步解决:
第一步:先让被调用服务把错误消息返回给调用方
默认情况下,Spring Boot如果抛出未处理的RuntimeException,会返回一个500错误,但这个响应里的消息不会直接是你抛出的"Already modified"——要么是默认的错误页面(HTML),要么是包含通用字段的JSON,但原消息可能没被正确暴露出来。所以得让被调用服务主动把异常消息放到HTTP响应体中:
方法1:用全局异常处理器(推荐)
在被调用服务里写一个全局异常处理类,捕获RuntimeException并返回带消息的响应:
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(RuntimeException.class) public ResponseEntity<String> handleRuntimeException(RuntimeException e) { // 选一个合适的状态码,比如409(冲突)比500更贴合"已被修改"的场景 return ResponseEntity.status(HttpStatus.CONFLICT).body(e.getMessage()); } }
方法2:自定义异常+@ResponseStatus
如果想更规范,可以自定义一个异常类,用注解指定状态码:
@ResponseStatus(HttpStatus.CONFLICT) // 返回409状态码 public class AlreadyModifiedException extends RuntimeException { public AlreadyModifiedException(String message) { super(message); } }
然后被调用服务里抛出这个自定义异常:
throw new AlreadyModifiedException("Already modified");
这种方式下,Spring会自动生成包含原消息的JSON响应(结构类似{"timestamp":"...","status":409,"error":"Conflict","message":"Already modified","path":"/xxx"})。
第二步:调用方从响应体提取原错误消息
现在调用方的问题是,RestClientResponseException.getMessage()返回的是封装了请求URL和状态的通用提示,比如"I/O error on PUT request for ...",但真正的错误消息在响应体里。所以要修改调用方的catch逻辑:
情况1:被调用方返回纯文本消息(对应方法1)
直接读取响应体字符串:
try { response = restTemplate.exchange(urlModifyAttributes, HttpMethod.PUT, new HttpEntity<>(attributesMap, headerMap), parameterizedTypeReference, uriVars); return response.getBody(); } catch (RestClientResponseException e) { // 读取响应体里的原错误消息 String originalErrorMessage = e.getResponseBodyAsString(); log.error("Modify attributes failed. Status code: {}, Original error: {}", e.getStatusCode(), originalErrorMessage, e); throw new RuntimeException(originalErrorMessage); } catch (Exception e) { log.error("Modify attributes failed. Error: {}", e.getMessage(), e); throw new RuntimeException(e.getMessage()); }
情况2:被调用方返回JSON格式错误(对应方法2)
需要解析JSON拿到message字段,先写一个对应错误结构的POJO:
class ErrorResponse { private String message; // getter和setter public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
然后在catch块里解析:
catch (RestClientResponseException e) { ObjectMapper objectMapper = new ObjectMapper(); try { ErrorResponse errorResponse = objectMapper.readValue(e.getResponseBodyAsString(), ErrorResponse.class); String originalErrorMessage = errorResponse.getMessage(); log.error("Modify attributes failed. Status code: {}, Original error: {}", e.getStatusCode(), originalErrorMessage, e); throw new RuntimeException(originalErrorMessage); } catch (JsonProcessingException ex) { // 解析失败时降级处理 log.error("Failed to parse error response", ex); throw new RuntimeException("Modify attributes failed", e); } }
这样调整后,调用方就能拿到被调用服务抛出的"Already modified"原消息了~
内容的提问来源于stack exchange,提问作者User_Targaryen

