Spring @RestControllerAdvice无法处理400状态码异常问题排查
问题原因
出现这个问题核心有两个触发点:
- 你继承的
ResponseEntityExceptionHandler父类本身已经内置了大量Spring MVC标准异常的处理逻辑,所有会返回400状态码的内置异常(包括校验抛出的ConstraintViolationException、参数格式错误、请求体解析失败等),都会优先被父类的异常处理方法拦截,直接返回默认格式的400响应,不会走到你自定义的badRequestException方法中。 - 你定义的两个异常处理方法都使用了
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
相关产品推荐
相关产品推荐

