一次文件上传异常的踩坑、定位与修复复盘(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
}

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

相关推荐
NGINX开源社区4 分钟前
使用 Microsoft Entra ID 配置 NGINX Plus 以实现 SAML SSO
后端·python·flask
小鸡吃米…7 分钟前
基于 TensorFlow 的图像识别
人工智能·python·tensorflow
匠心网络科技9 分钟前
JavaScript进阶-ES6 带来的高效编程新体验
开发语言·前端·javascript·学习·面试
儒雅芝士19 分钟前
RethinkFun深度学习笔记
人工智能·笔记·深度学习
一只大袋鼠24 分钟前
并发编程(三):线程快照统计・grep+awk+sort+uniq 实战详解
java·开发语言·多线程·并发编程
莫寒清29 分钟前
Mybatis的插件原理
面试·mybatis
unfeeling_29 分钟前
Tomcat实验
java·tomcat
小鸡吃米…29 分钟前
TensorFlow - 构建计算图
人工智能·python·tensorflow
土拨鼠烧电路37 分钟前
笔记12:AI在快消:超越概念的四大落地场景
人工智能·笔记
Hx_Ma1641 分钟前
前台模块以及分页逻辑
java·开发语言