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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 07:11:12