Spring Boot 3.2.5中@RestControllerAdvice无法获取错误状态码与请求URI
解决方案
问题根源在于:当@RestControllerAdvice直接捕获异常并返回响应时,Spring Boot的默认错误处理流程(包括DefaultErrorAttributes填充servlet.error系列属性)不会被触发——因为异常已经被Advice接管,底层的错误属性设置逻辑没有执行。以下是几种可行的实现方式:
方案一:记录日志后重新抛出异常,复用默认错误流程
这种方式最简单,完全依赖Spring Boot原生错误处理能力,仅在Advice中完成日志记录,不干预响应生成:
@RestControllerAdvice public class GlobalExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); @ExceptionHandler(Exception.class) public void handleException(Exception ex, HttpServletRequest request) throws Exception { // 记录异常详情 logger.error("请求URI: {} 处理失败", request.getRequestURI(), ex); // 重新抛出异常,交给Spring Boot默认错误处理流程 throw ex; } // 针对自定义异常单独处理(可选) @ExceptionHandler(BusinessException.class) public void handleBusinessException(BusinessException ex, HttpServletRequest request) throws BusinessException { logger.error("业务异常,URI: {},错误码: {}", request.getRequestURI(), ex.getErrorCode(), ex); throw ex; } }
优点:零自定义响应逻辑,完全复用默认错误格式;缺点:无法修改响应体内容,只能依赖原生输出。
方案二:手动填充错误属性,复用DefaultErrorAttributes生成标准响应
如果需要自定义响应内容但又想保留默认错误字段(如timestamp、status、path等),可以手动填充servlet.error属性后,调用DefaultErrorAttributes生成响应体:
@RestControllerAdvice public class GlobalExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); private final DefaultErrorAttributes errorAttributes; // 注入Spring Boot默认的ErrorAttributes实例 public GlobalExceptionHandler(DefaultErrorAttributes errorAttributes) { this.errorAttributes = errorAttributes; } @ExceptionHandler(Exception.class) public ResponseEntity<Map<String, Object>> handleException(Exception ex, HttpServletRequest request, HttpServletResponse response) { logger.error("请求URI: {} 发生异常", request.getRequestURI(), ex); // 手动填充servlet错误属性,让DefaultErrorAttributes能读取到 HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; request.setAttribute("jakarta.servlet.error.status_code", status.value()); request.setAttribute("jakarta.servlet.error.request_uri", request.getRequestURI()); request.setAttribute("jakarta.servlet.error.exception", ex); // 生成标准错误响应体 Map<String, Object> errorResponse = errorAttributes.getErrorAttributes( new ServletWebRequest(request, response), ErrorAttributeOptions.defaults() ); // 可选:添加自定义字段 errorResponse.put("traceId", request.getHeader("X-Trace-Id")); return new ResponseEntity<>(errorResponse, status); } // 处理自定义业务异常,设置对应状态码 @ExceptionHandler(BusinessException.class) public ResponseEntity<Map<String, Object>> handleBusinessException(BusinessException ex, HttpServletRequest request, HttpServletResponse response) { logger.error("业务异常,URI: {},错误码: {}", request.getRequestURI(), ex.getErrorCode(), ex); HttpStatus status = HttpStatus.BAD_REQUEST; request.setAttribute("jakarta.servlet.error.status_code", status.value()); request.setAttribute("jakarta.servlet.error.request_uri", request.getRequestURI()); request.setAttribute("jakarta.servlet.error.exception", ex); Map<String, Object> errorResponse = errorAttributes.getErrorAttributes( new ServletWebRequest(request, response), ErrorAttributeOptions.defaults() ); errorResponse.put("errorCode", ex.getErrorCode()); return new ResponseEntity<>(errorResponse, status); } }
注意:Spring Boot 3.x用jakarta.servlet.error.*属性,Spring Boot 2.x对应javax.servlet.error.*,需根据版本调整。
方案三:继承ResponseEntityExceptionHandler,统一处理异常
适合需要同时处理Spring MVC原生异常(如MethodArgumentNotValidException、HttpRequestMethodNotSupportedException)和自定义异常的场景,能自动生成符合默认格式的响应:
@RestControllerAdvice public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); // 处理Spring MVC原生异常 @Override protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) { String requestUri = ((ServletWebRequest) request).getRequest().getRequestURI(); logger.error("请求URI: {} 发生异常,状态码: {}", requestUri, status.value(), ex); // 调用父类方法生成默认响应体,也可以自定义body return super.handleExceptionInternal(ex, body, headers, status, request); } // 处理自定义业务异常 @ExceptionHandler(BusinessException.class) public ResponseEntity<Object> handleBusinessException(BusinessException ex, WebRequest request) { String requestUri = ((ServletWebRequest) request).getRequest().getRequestURI(); logger.error("业务异常,URI: {},错误码: {}", requestUri, ex.getErrorCode(), ex); // 构建符合默认格式的响应体 Map<String, Object> body = new HashMap<>(); body.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); body.put("status", HttpStatus.BAD_REQUEST.value()); body.put("error", HttpStatus.BAD_REQUEST.getReasonPhrase()); body.put("message", ex.getMessage()); body.put("path", requestUri); body.put("errorCode", ex.getErrorCode()); return handleExceptionInternal(ex, body, new HttpHeaders(), HttpStatus.BAD_REQUEST, request); } }
内容的提问来源于stack exchange,提问作者Pp88
相关产品推荐
相关产品推荐

