c -> true 导致异常返回 404 问题排查

最近在新开一个项目,在新建基础架构的时候遇到了一个很有意思的问题:

  • 接口正常访问时返回 200
  • Controller 中人为制造异常后
  • 后端日志明确打印了异常
  • 但前端(Apifox)收到的却是 404
  • 理论上应该返回 500

最终排查发现:

根因是 Spring Boot 全局路径前缀配置污染了系统内置的 /error 路由。

这个问题非常具有迷惑性,记录一下完整排查过程。

一、问题现象

Controller层制造异常,用于测试异常处理 :

java 复制代码
int a = 1 / 0;

二、后端日志

java 复制代码
java.lang.ArithmeticException: / by zero

完整调用链中可以看到:

java 复制代码
Servlet.service() for servlet [dispatcherServlet]
threw exception

说明:

  • Controller 已经执行
  • 异常已经抛出
  • Spring MVC 已经捕获异常

按正常逻辑:

应该返回:

java 复制代码
500 Internal Server Error

但实际上:

Apifox 返回:

java 复制代码
404 Not Found

三、第一次怀疑方向

一开始怀疑:

1. AOP 吞异常

因为项目里有:

java 复制代码
@Around
public Object around(ProceedingJoinPoint point)

很多项目会写成:

java 复制代码
try {
    return point.proceed();
} catch (Exception e) {

}
return null;

这种会导致:

  • Spring 找不到返回视图
  • 最终变成 404

但检查后发现:AOP 没问题

2. 全局异常处理器

怀疑:

java 复制代码
@ControllerAdvice

或者:

java 复制代码
@ExceptionHandler

吞掉异常

检查后排除

四、真正的问题

项目中有如下配置:

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {

        // 为所有 Controller 添加 /api 前缀
        configurer.addPathPrefix("/api", c -> true);
    }
}

问题就出在:c -> true

五、问题原理分析

这段代码的意思是:

给所有 Controller 都加 /api 前缀。

注意:

这里的"所有 Controller"不仅仅包括:

  • 自己写的 Controller

还包括:

  • Spring Boot 内置 Controller
  • BasicErrorController
  • /error
  • actuator
  • swagger 等

六、为什么正常接口还能返回 200?

正常情况下请求流程:

DispatcherServlet

-> Controller

-> return AjaxResult.success()

-> 200

因为没有异常:

  • 不会进入 /error
  • 所以一切正常

因此返回:200 OK

七、为什么异常时变成 404?

出现异常后,Spring MVC 流程:

DispatcherServlet

-> Controller 抛异常

-> 异常解析器

-> 转发到 /error

正常应该:

/error

-> BasicErrorController

-> 返回 500

但由于:

java 复制代码
addPathPrefix("/api", c -> true)

Spring 内置:

/error

也被加了前缀:

/api/error

现在问题就来了:

Spring Boot 默认真正注册的是:/error;不是/api/error

于是异常发生后:

/api/error

找不到。

最终:

404 Not Found

八、问题本质

这里的 404:并不是业务接口不存在;而是异常处理路由不存在

九、最终解决方案

不要给所有 Controller 都加前缀。

应该只给自己的业务 Controller 加。

修改为:

@Configuration

public class WebConfig implements WebMvcConfigurer {

@Override

public void configurePathMatch(PathMatchConfigurer configurer) {

configurer.addPathPrefix(

"/api",

c -> c.getPackageName().startsWith("com.bs.web.controller")

);

}

}

这样:

  • 自己业务 Controller:

    /api/xxx

  • Spring 系统 Controller:

    /error

不会受影响

总结

c -> true 这种全匹配不要随便使用,Spring Boot 的 /error一旦被污染各种诡异问题都会出现

相关推荐
qq_375916371 小时前
kettle菜鸟教程
开发语言·kettle
qq_254674412 小时前
Alpine Linux 基于 Debian 等系统的常规 Nginx
开发语言
故事和你912 小时前
洛谷-数据结构2-1-二叉堆与树状数组1
开发语言·数据结构·c++·算法·动态规划·图论
挨踢ren2 小时前
C++虚函数:从基础到高阶
java·开发语言·jvm
hhb_6182 小时前
C语言核心技术难点梳理与实战案例解析
c语言·开发语言
海参崴-2 小时前
C++ STL篇 红黑树的模拟实现
开发语言·c++
Dshuishui2 小时前
我用 Claude Code 做了一个学术论文搜索工具
开发语言·人工智能·python·pip·uv
Resky08182 小时前
ReentrantReadWriteLock 深度解析
java·开发语言·juc
笨笨饿2 小时前
#72_聊聊I2C以及他们的变体
linux·c语言·网络·stm32·单片机·算法·个人开发