一次文件上传异常的踩坑、定位与修复复盘(Spring Boot + 接口测试)

本文记录一次真实的接口质量问题:文件超限时接口返回 200 + 业务码 -999,以及我是如何通过测试发现问题、分析原因、推动后端改造异常处理机制的全过程。既包含问题定位方法,也提供可复用的改造方案,适合 Spring Boot 开发者与接口测试工程师参考。


一、业务背景

在个人博客项目中,我负责测试封面图片上传接口

复制代码
POST /user/uploadCover

业务流程如下:

  1. 前端上传图片文件
  2. 后端接收 MultipartFile
  3. 校验文件格式与大小
  4. 上传至七牛云
  5. 返回图片访问 URL

关键配置(application.yml):

yaml 复制代码
spring:
  servlet:
    multipart:
      enabled: true
      max-file-size: 5MB
      max-request-size: 35MB

初版接口核心代码(简化版)如下:

java 复制代码
@PostMapping("/user/uploadCover")
public Result uploadCover(@RequestParam(value="file", required=false) MultipartFile file) {

    if (file == null || file.isEmpty()) {
        return Result.fail(400,"上传文件不能为空");
    }

    String suffix = StringUtils.substringAfterLast(file.getOriginalFilename(), ".");
    if (!Arrays.asList("png","jpg","jpeg","gif","bmp")
            .contains(suffix.toLowerCase())) {
        return Result.fail(400, "仅支持图片格式文件");
    }

    String fileName = UUID.randomUUID() + "." + suffix;

    if (!qiniuUtils.upload(file, fileName)) {
        return Result.fail(FILE_UPLOAD_ERROR.getCode(),
                           FILE_UPLOAD_ERROR.getMsg());
    }

    return Result.success(QiniuUtils.url + fileName);
}

二、测试发现异常行为

我编写了一个超大文件上传的反向测试用例(伪代码):

python 复制代码
def test_upload_oversize_file(request_util, auth_token):
    with open("big_image_8MB.jpg", "rb") as f:
        files = {"file": f}
        response = request_util.post("/user/uploadCover", files=files)

    json_data = response.json()
    assert response.status_code == 200
    assert json_data["code"] == 413

实际返回结果却是:

json 复制代码
{
  "success": false,
  "code": -999,
  "msg": "系统异常,请联系管理员哦~",
  "data": null
}

表面上测试"通过",但明显存在质量问题:

  • 文件超过 5MB 时,Spring 抛出了 MaxUploadSizeExceededException
  • 但最终被全局异常兜底成 -999
  • HTTP 状态码却仍然是 200

这意味着:

用户看不到"文件太大"的明确提示,测试也没有真正发挥质量门禁作用。

因此,我将其判定为有效缺陷,并开始定位根因。


三、从日志定位根本原因

关键日志如下:

复制代码
Resolved [MaxUploadSizeExceededException: Maximum upload size exceeded]

说明:

  • 异常发生在 Spring 解析 multipart 阶段
  • 请求尚未进入 Controller

随后又出现:

复制代码
No static resource user/user/uploadCover

说明:

  • 异常触发后,DispatcherServlet 发生了转发
  • 请求并未正常进入业务 Controller

核心结论:

这个异常发生在 Controller 之外,因此写在 Controller 层的异常处理器根本"看不到"它。


四、为什么原有全局异常处理不生效

原有异常处理类:

java 复制代码
@ControllerAdvice
public class AllExceptionHandler {
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result doException(Exception ex){
        ex.printStackTrace();
        return Result.fail(-999,"系统异常,请联系管理员哦~");
    }

    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public Result handleMaxSizeException(MaxUploadSizeExceededException e) {
        return Result.fail(413, "文件超过最大限制 5MB");
    }
}

存在两个关键问题:

❌ 问题 1:注解不够合适

@ControllerAdvice 不保证始终返回 JSON,某些场景可能走视图解析或转发逻辑。

更合适的做法是:

java 复制代码
@RestControllerAdvice

它等价于:

复制代码
@ControllerAdvice + @ResponseBody

更适合接口项目。


❌ 问题 2:异常发生阶段不匹配

MaxUploadSizeExceededException 是:

  • Servlet 层 / Spring Multipart 解析层异常
  • 不是 Controller 内部异常

因此,普通的 ControllerAdvice 无法稳定拦截,需要更强的 RestControllerAdvice + 专门的异常处理方法


五、为什么"只改一个地方就好了"

很多人会问:

为什么只把 @ControllerAdvice 改成 @RestControllerAdvice 就解决了?

本质上,你改的不是"细节",而是异常拦截的层级

改之前

复制代码
文件太大
   ↓
Spring Multipart 解析异常
   ↓
ControllerAdvice:我看不到 👀
   ↓
系统兜底 → code = -999

改之后

复制代码
文件太大
   ↓
Spring Multipart 解析异常
   ↓
RestControllerAdvice:我能看见 ✅
   ↓
你的处理器 → 返回 413

一句话总结:

你不是修了一个"小 bug",而是把异常处理放到了正确的位置


六、最终修复方案(可直接复用)

✅ 改造一:统一异常处理器

java 复制代码
@RestControllerAdvice   // 关键改动
public class AllExceptionHandler {

    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public Result handleMaxSizeException(MaxUploadSizeExceededException e) {
        return Result.fail(413, "文件超过最大限制 5MB");
    }

    @ExceptionHandler(Exception.class)
    public Result doException(Exception ex){
        ex.printStackTrace();
        return Result.fail(-999,"系统异常,请联系管理员哦~");
    }
}

✅ 改造二:保持 Multipart 配置

yaml 复制代码
spring:
  servlet:
    multipart:
      enabled: true
      max-file-size: 5MB
      max-request-size: 35MB

✅ 修复后接口表现

上传超过 5MB 的文件时,返回:

json 复制代码
{
  "success": false,
  "code": 413,
  "msg": "文件超过最大限制 5MB",
  "data": null
}

这才是可被前端、测试、运维识别的清晰业务异常

相关推荐
极客小云1 小时前
【基于AI的自动商品试用系统:不仅仅是虚拟试衣!】
javascript·python·django·flask·github·pyqt·fastapi
JMchen1231 小时前
Android相机硬件抽象层(HAL)逆向工程:定制ROM的相机优化深度指南
android·开发语言·c++·python·数码相机·移动开发·android studio
丝斯20111 小时前
AI学习笔记整理(69)——物理AI中世界模型
人工智能·笔记·学习
lang201509281 小时前
一键生成Java Web项目:Tomcat-Maven原型解析
java·前端·tomcat
heartbeat..1 小时前
JVM 参数配置指南:内存调优、收集器选择与问题排查
java·运维·jvm·性能优化
娇娇乔木1 小时前
模块九--static/可变参数/递归/冒泡排序/二分查找/对象数组/方法参数/快速生成方法/debug--尚硅谷Javase笔记总结
java·开发语言
不会代码的小测试2 小时前
UI自动化-Grid分布式运行
运维·分布式·python·selenium·自动化
indexsunny2 小时前
互联网大厂Java面试实录:Spring Boot微服务与Kafka消息队列实战解析
java·spring boot·微服务·面试·kafka·电商·技术解析
浅碎时光8072 小时前
Qt (信号与槽 Widget控件 qrc文件)
开发语言·qt