springboot 3.x MultipartFile 参数总是为空 参数传递不进来
场景:
在写一个统一文件上传的时候,MultipartFile参数一直传递不进来,一直为空。
排错方法:
我先是将业务简化,使用api文档进行测试,测试是前端问题还是后端问题。
简化后业务:
用apifox文档测试之后,还是显示报错
{ "code": 500, "message": "Required part 'file' is not present.", "result": null, "type": "error" }
代表其实servlet请求体里根本没有这个文件流参数,跟文件名和参数名没有太大关系,而且文件上传springboot是默认打开的,也不需要显示的去打开
# 数据源配置 servlet: multipart: enabled: true max-file-size: 20MB max-request-size: 20MB
之后一直根据这个报错信息去查找相关文档,发现出现这个问题的有很多种原因,那就一个一个去排查
1.前后端参数名对应不一致
2.spring.servlet.multipart.enabled=false即关闭文件上传支持
3.配置文件中指定了文件上传时的大小值问题
4.切换内嵌容器tomcat到undertow的配置问题
5.spring.servlet.multipart.location=/tmp指定了临时文件站,但路径不存在
6.多次读取HttpServletRequest流
7.springboot已经有CommonsMultipartResolver,需要排除原有的Multipart配置@EnableAutoConfiguration(exclude = {MultipartAutoConfiguration.class})
一个个排查之后,发现自己在请求体到接口之前就被使用过多次,很符合多次读取HttpServletRequest流的问题
就是这里使用了:
import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; /** * CachedBodyHttpServletRequest 扩展了 HttpServletRequestWrapper 类, * 用于缓存请求体,使其可以被多次读取。 */ public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper { private final byte[] cachedBody; /** * 构造函数,读取请求体并进行缓存。 * * @param request 原始的 HttpServletRequest * @throws IOException 如果发生 I/O 错误 */ public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException { super(request); // 读取请求体并将其转换为字节数组 InputStream requestInputStream = request.getInputStream(); this.cachedBody = requestInputStream.readAllBytes(); }
发现了问题,就好解决了,找到在哪里调用了这个请求体缓存,写这个缓存的目的也是为了解决请求体不能被多次使用的问题,因为我有一个自动检测验证码的拦截器需要去拦截登录接口的参数,
之前是方法没有注意,本来是不应该有这个错误的,第一个是没有正确在缓存里配置文件流的操作,第二个是验证码拦截器并没有针对登录接口专门做,而是都拦截了下来,导致都是用的这个缓存
之后就修改一下代码就好了,让这个拦截器只拦截登录接口的时候处理业务:
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 检查请求 URL 和方法 String url = request.getRequestURI(); if ("/bt/login".equals(url) && request.getMethod().equals("POST")) { // 包装请求,缓存请求体 CachedBodyHttpServletRequest cachedBodyHttpServletRequest = new CachedBodyHttpServletRequest(request); try { // 解析请求体 LoginUserDto loginUserDto = JSON.parseObject(cachedBodyHttpServletRequest.getReader(), LoginUserDto.class); // 验证码校验逻辑 if (loginUserDto.getCode() == null || !loginUserDto.getCode().equals(redisUtil.get(RedisKeys.CAPTCHA + loginUserDto.getUuid()))) { throw new CaptchaException("验证码错误"); } // 验证码正确,删除验证码 redisUtil.del(RedisKeys.CAPTCHA + loginUserDto.getUuid()); } catch (CaptchaException e) { // 交给认证失败处理器 loginFailureHandler.onAuthenticationFailure(cachedBodyHttpServletRequest, response, e); return; } // 继续处理请求 filterChain.doFilter(cachedBodyHttpServletRequest, response); } filterChain.doFilter(request, response); }
到此,顺利解决问题。
{ "code": 0, "message": "操作成功", "result": "url访问地址...", "type": "success" }
分类:
报错踩坑集
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!