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一旦被污染各种诡异问题都会出现

相关推荐
xiaoye-duck24 分钟前
《C++ 项目:手写大模型接入 SDK》LLMManager架构设计与实现
开发语言·网络·c++·语言模型
一条大祥脚1 小时前
CuTe 与 CuTe DSL:从 C++ 模板元编程到 Python DSL 的 GPU 编程演进
开发语言·c++·python·cutlass·cute·cute dsl
·present·1 小时前
CSAPP(lecture 13_02 Linking)
c语言·c++
FRing032 小时前
vue2干货总结8(路由模块封装、router-link、声明式导航、路由重定向、编程式导航)
开发语言·前端·vue.js·vue2
旖旎夜光2 小时前
【C++ 基础入门】C++ 多态(上):虚函数、重写、override/final 与抽象类
开发语言·c++·笔记·学习
starzy19902 小时前
深入理解Scala嵌套函数:作用域封装之道
开发语言·scala
Sakura_j_h_2 小时前
从零写一个 MCP stdio Server:用 Python 校验博客文章格式
开发语言·python·自动化·ai工具·mcp·json-rpc
白杨尚青4 小时前
C++进阶篇(五):红黑树——四条规则、三种插入情况与旋转变色
开发语言·数据结构·c++·stl·红黑树·平衡二叉树
.道阻且长.4 小时前
Linux基本开发工具:gdb和cgdb的使用
linux·运维·服务器·开发语言
starzy19904 小时前
深入理解Scala匿名函数:函数式编程基石
开发语言·scala