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

Spring @RestControllerAdvice无法处理400状态码异常问题排查

问题原因

出现这个问题核心有两个触发点:

  1. 你继承的ResponseEntityExceptionHandler父类本身已经内置了大量Spring MVC标准异常的处理逻辑,所有会返回400状态码的内置异常(包括校验抛出的ConstraintViolationException、参数格式错误、请求体解析失败等),都会优先被父类的异常处理方法拦截,直接返回默认格式的400响应,不会走到你自定义的badRequestException方法中。
  2. 你定义的两个异常处理方法都使用了protected访问修饰符,Spring的全局异常处理器只会扫描注册public修饰的@ExceptionHandler方法,非public方法不会被加入异常处理映射表,部分场景下无法被触发。你自定义的404类异常因为父类没有对应的处理逻辑,侥幸能走到方法中,但逻辑本身是不符合Spring扫描规则的。

另外你代码中用ex?.message!!做非空断言有很大概率触发空指针,异常的message字段本身允许为null,强转会直接导致服务报错。

修复方案

两种方案选其一即可:

方案一:保留继承关系,补全逻辑

  • 把两个异常处理方法的访问修饰符从protected改为public
  • 重写父类的handleExceptionInternal方法,覆盖父类默认的异常响应逻辑,让父类接管的400类异常也返回你自定义的ErrorsDetails格式
  • 去掉危险的非空断言,给message字段加空值兜底

修复后的参考代码:

@RestControllerAdvice
class RestResponseEntityExceptionHandler : ResponseEntityExceptionHandler() {
    @ExceptionHandler(
        value = [
            NotFoundException::class,
            AccountNotFoundException::class,
            ExecutionLogNotFoundException::class,
            SubscriptionNotFoundException::class
        ]
    )
    fun notFoundException(
        ex: Exception,
        request: WebRequest
    ): ResponseEntity<ErrorsDetails> {
        val errorDetails = ErrorsDetails(ex.message ?: "请求的资源不存在")
        return ResponseEntity(errorDetails, HttpStatus.NOT_FOUND)
    }

    @ExceptionHandler(
        value = [
            AccountIsNotConnectedException::class,
            AccountCannotBeDeletedException::class,
            InvalidVerificationSignatureException::class,
            SubscriptionAlreadyExistsException::class,
            ConstraintViolationException::class
        ]
    )
    fun badRequestException(
        ex: Exception,
        request: WebRequest
    ): ResponseEntity<ErrorsDetails> {
        val errorDetails = ErrorsDetails(ex.message ?: "请求参数不合法")
        return ResponseEntity(errorDetails, HttpStatus.BAD_REQUEST)
    }

    override fun handleExceptionInternal(
        ex: Exception,
        body: Any?,
        headers: HttpHeaders,
        status: HttpStatus,
        request: WebRequest
    ): ResponseEntity<Any> {
        // 统一覆盖父类处理的所有异常的响应格式
        val errorDetails = ErrorsDetails(ex.message ?: "请求处理失败")
        return ResponseEntity(errorDetails, headers, status)
    }
}

方案二:移除继承关系,完全自定义处理

如果你不需要ResponseEntityExceptionHandler提供的默认异常处理逻辑,直接去掉类声明上的继承关系即可。这种方式下你写的所有@ExceptionHandler方法会全权接管异常处理,不会出现父类逻辑抢占的问题,只需要把方法修饰符改成public、去掉非空断言就能正常运行。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 22:33:22