💜 想真正擅长编程? 从一门课程开始,100% 免费

3-5、接口404-500报错3步定位bug

写完代码启动项目,访问接口返回404或500,这是开发中最常见的场景。这节教你3步快速定位问题。


一、先搞清楚404和500的区别

状态码 含义 通俗解释
404 Not Found 请求的地址找不到,路径对不上
500 Internal Server Error 服务器内部出错了,代码有bug
401 Unauthorized 没登录或Token过期
403 Forbidden 登录了但没权限
400 Bad Request 请求参数不对

404是路径问题,500是代码问题。 定位思路完全不同。


二、404定位:3步找到问题

第1步:确认请求URL

F12 → Network → 找到报404的请求 → 看Request URL:

TEXT
Request URL: http://localhost:8080/api/user/list

记录下完整的URL路径。

第2步:确认后端路由

在后端代码里找到对应的Controller:

Java
@RestController
@RequestMapping("/api/user")
public class UserController {

    @GetMapping("/list")
    public Result list() {
        return userService.list();
    }
}

完整路径 = 类上的@RequestMapping + 方法上的@GetMapping/@PostMapping

所以这个接口的完整路径是:/api/user/list

第3步:对比路径

检查点 常见错误
路径拼写 /user/list vs /user/List(大小写)
前后斜杠 /api/user/list vs /api/user/list/(尾部斜杠)
context-path 配了/api但请求没加,或反过来
请求方法 后端是@GetMapping但前端用POST

404常见原因

原因1:context-path没配对

YAML
server:
  servlet:
    context-path: /api

配了context-path后,所有接口前面都要加/api:

  • 后端@RequestMapping("/user") → 实际路径是/api/user
  • 前端请求要发到http://localhost:8080/api/user

原因2:请求方法不匹配

Java
@PostMapping("/add")  // 后端只接受POST

前端发GET请求就404:

JavaScript
// 错误:用了GET
axios.get('/api/user/add')

// 正确:用POST
axios.post('/api/user/add', data)

原因3:Controller没被扫描到

SpringBoot默认扫描启动类所在包及其子包。如果Controller在启动类包的外面,扫描不到。

Java
// 启动类在 com.company.project
@SpringBootApplication
public class App { ... }

// Controller在 com.company.controller(不在project的子包下)
@RestController  // 不会被扫描到!

解决:加扫描注解

Java
@SpringBootApplication(scanBasePackages = "com.company")

或

Java
@ComponentScan("com.company")

原因4:接口被拦截器拦了

有些项目有拦截器或过滤器,没登录的请求直接返回404或401。

Java
@Component
public class AuthInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, ...) {
        String token = request.getHeader("Authorization");
        if (token == null) {
            response.setStatus(401);
            return false;  // 拦截
        }
        return true;
    }
}

检查拦截器配置,确认测试接口没有被拦截。


三、500定位:3步找到bug

第1步:看响应体

F12 → Network → 找到500的请求 → Response标签页:

JSON
{
  "timestamp": "2026-01-01T10:00:00.000+00:00",
  "status": 500,
  "error": "Internal Server Error",
  "message": "Cannot invoke \"String.length()\" because \"str\" is null",
  "path": "/api/user/info"
}

message字段通常有报错信息,告诉你具体哪里出错了。

第2步:看后端日志

IDEA控制台搜索ERROR或Exception,找到完整的错误堆栈:

TEXT
java.lang.NullPointerException: Cannot invoke "String.length()" because "str" is null
    at com.company.project.service.impl.UserServiceImpl.getInfo(UserServiceImpl.java:45)
    at com.company.project.controller.UserController.info(UserController.java:30)
    ...

看堆栈的技巧:找你自己写的代码。

堆栈里会有很多Spring框架内部的方法调用,忽略它们,找com.公司名开头的行:

TEXT
at com.company.project.service.impl.UserServiceImpl.getInfo(UserServiceImpl.java:45)

这行告诉你:UserServiceImpl类的第45行出了空指针。

第3步:打断点调试

在报错的那行代码打断点:

  1. 找到UserServiceImpl.java第45行
  2. 左边栏点一下,出现红点
  3. 重新请求接口
  4. 程序停在断点处
  5. 查看变量值,确认哪个是null
  6. 找到根因,修复

四、500常见原因

原因1:空指针异常(最常见)

TEXT
java.lang.NullPointerException

常见场景:

Java
// 场景1:对象没注入
@Autowired
private UserService userService;  // userService为null

// 场景2:方法返回null
String name = user.getName();  // user为null

// 场景3:集合为null
List<User> list = mapper.selectList(null);
int size = list.size();  // list可能为null

排查方法: 打断点看哪个变量是null,加上null判断:

Java
if (user != null && user.getName() != null) {
    // 处理逻辑
}

或用Optional:

Java
String name = Optional.ofNullable(user)
    .map(User::getName)
    .orElse("default");

原因2:参数类型转换错误

TEXT
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

常见场景:

Java
// 从Redis取数据,类型不对
Object obj = redisTemplate.opsForValue().get("key");
Integer num = (Integer) obj;  // obj实际是String

解决: 用安全转换:

Java
String str = String.valueOf(obj);
Integer num = Integer.parseInt(str);

原因3:SQL执行出错

TEXT
java.sql.SQLSyntaxErrorException: Table 'my_project.user' doesn't exist

或:

TEXT
BadSqlGrammarException: ...

排查:

  1. 表是否存在:SHOW TABLES;
  2. 字段名是否正确
  3. SQL语法是否正确

MyBatis开启SQL打印,看实际执行的SQL:

YAML
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

原因4:JSON序列化/反序列化错误

TEXT
HttpMessageNotReadableException: JSON parse error

常见原因:

  • 前端传的JSON格式不对
  • JSON字段名和Java类属性名不匹配
  • 日期格式不对

排查: F12 → Network → Payload,看前端传的JSON:

JSON
{
  "username": "test",
  "createTime": "2026-01-01 10:00:00"  // 格式可能不对
}

后端用@JsonFormat指定格式:

Java
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;

原因5:依赖注入失败

TEXT
 UnsatisfiedDependencyException: Error creating bean with name 'xxx'

原因: Spring容器创建Bean时失败了。

排查:

  1. 看具体的Caused by信息
  2. 检查@Service、@Component等注解是否加了
  3. 检查构造方法或@Autowired的依赖是否能找到

五、调试技巧

1. 条件断点

不想每次请求都停,只在特定条件停:

右键断点 → Condition → 输入条件:

TEXT
userId != null && userId.equals(1001)

只有userId等于1001时才停。

2. 日志打印

不想打断点,加日志看执行过程:

Java
@PostMapping("/add")
public Result add(@RequestBody UserDTO dto) {
    log.info("收到请求,参数:{}", dto);
    // 处理逻辑
    Result result = userService.add(dto);
    log.info("处理完成,结果:{}", result);
    return result;
}

3. Postman调试

用Postman单独调试接口,排除前端干扰:

  1. 先调登录接口,拿到Token
  2. Token加到后续请求的Header里
  3. 逐个调试有问题的接口

4. 全局异常处理

项目应该有全局异常处理,统一返回格式:

Java
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public Result handleException(Exception e) {
        log.error("系统异常:", e);
        return Result.error("系统异常:" + e.getMessage());
    }

    @ExceptionHandler(NullPointerException.class)
    public Result handleNullPointer(NullPointerException e) {
        log.error("空指针异常:", e);
        return Result.error("参数不能为空");
    }

    @ExceptionHandler(BusinessException.class)
    public Result handleBusiness(BusinessException e) {
        log.error("业务异常:", e);
        return Result.error(e.getMessage());
    }
}

有了全局异常处理,500错误也会返回统一的JSON格式,方便前端处理。


六、快速定位流程

404定位流程

TEXT
返回404
  │
  ├─ URL路径对吗? → 对比前端URL和后端@RequestMapping
  │    ├─ 不对 → 改路径
  │    └─ 对 ↓
  ├─ context-path加了吗? → URL需要加context-path
  │    ├─ 没加 → 加上
  │    └─ 加了 ↓
  ├─ 请求方法对吗? → GET/POST/PUT/DELETE
  │    ├─ 不对 → 改请求方法
  │    └─ 对 ↓
  ├─ Controller被扫描到了吗? → 检查包路径
  │    ├─ 没有 → 加scanBasePackages
  │    └─ 扫描了 ↓
  └─ 被拦截器拦了吗? → 检查拦截器配置

500定位流程

TEXT
返回500
  │
  ├─ 看Response的message → 有具体报错信息
  │
  ├─ 看后端日志 → 搜ERROR/Exception
  │    │
  │    ├─ NullPointerException → 空指针,找null变量
  │    ├─ ClassCastException → 类型转换错
  │    ├─ SQLSyntaxErrorException → SQL语法错
  │    ├─ HttpMessageNotReadableException → JSON格式错
  │    └─ UnsatisfiedDependencyException → Bean创建失败
  │
  └─ 打断点调试 → 找到根因 → 修复

小结

状态码 本质 定位方法
404 路径不对 对比前端URL和后端路由
500 代码有bug 看日志堆栈 + 断点调试

404查路径,500查代码。记住这个,遇到报错就不慌了。