You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot日志拦截器引发Multipart请求@RequestPart丢失问题

解决Spring Boot日志拦截器导致Multipart请求@RequestPart丢失的问题

问题根源在于你自定义的CachedHttpServletRequest没有处理Multipart请求的特殊逻辑:Spring在解析multipart/form-data请求后,会将原始HttpServletRequest包装为MultipartHttpServletRequest,而你的缓存类没有实现这个接口,也没有复用原始Multipart请求的part信息,导致后续控制器无法获取到@RequestPart指定的文件。

解决方案步骤:

  1. 让缓存类实现MultipartHttpServletRequest接口
    扩展你的CachedHttpServletRequest,实现Spring的MultipartHttpServletRequest接口,将Multipart相关的方法委托给原始的Multipart请求实例。

  2. 区分请求类型进行包装
    在拦截器中判断原始请求是否为MultipartHttpServletRequest,针对性地使用对应的缓存包装类。

代码示例

修改后的CachedHttpServletRequest

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import java.util.Collection;
import java.util.Map;

public class CachedHttpServletRequest extends HttpServletRequestWrapper implements MultipartHttpServletRequest {

    private final MultipartHttpServletRequest multipartRequest;
    // 保留你原来的缓存body相关字段和普通请求的构造方法

    // 新增Multipart请求专用构造方法
    public CachedHttpServletRequest(MultipartHttpServletRequest request) {
        super(request);
        this.multipartRequest = request;
        // 注意:Multipart请求的InputStream已被Spring解析消耗,不要尝试重新读取缓存body
        // 如果需要记录Multipart请求的元数据,可通过multipartRequest获取文件/参数信息
    }

    // 实现MultipartHttpServletRequest的所有方法,直接委托给原始实例
    @Override
    public MultipartFile getFile(String name) {
        return multipartRequest.getFile(name);
    }

    @Override
    public Collection<MultipartFile> getFiles(String name) {
        return multipartRequest.getFiles(name);
    }

    @Override
    public Map<String, MultipartFile> getFileMap() {
        return multipartRequest.getFileMap();
    }

    @Override
    public Iterable<String> getFileNames() {
        return multipartRequest.getFileNames();
    }

    @Override
    public String getMultipartContentType(String paramOrFileName) {
        return multipartRequest.getMultipartContentType(paramOrFileName);
    }

    // 其他如getParameter相关方法可复用父类或直接委托,根据你的需求调整
}

调整拦截器的doFilter方法

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

public class RequestLoggingFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        HttpServletRequest wrappedRequest;

        // 判断是否为Multipart请求,使用对应的包装类
        if (request instanceof MultipartHttpServletRequest) {
            wrappedRequest = new CachedHttpServletRequest((MultipartHttpServletRequest) request);
        } else {
            // 普通请求的包装逻辑,复用你原来的代码
            wrappedRequest = new CachedHttpServletRequest(request);
        }

        // 执行你的日志记录逻辑,比如记录请求URL、参数等
        // 注意Multipart请求不要读取InputStream,避免数据丢失

        filterChain.doFilter(wrappedRequest, response);

        // 记录响应日志的逻辑
    }
}

关键注意事项

  • 不要重复读取Multipart请求的InputStream:Spring的MultipartResolver已经解析过请求体,InputStream已被消耗,强行读取会导致后续无法获取文件。如果需要记录Multipart请求的信息,直接通过MultipartHttpServletRequest的方法获取文件名、文件大小等元数据即可。
  • 调整拦截器执行顺序:确保你的日志拦截器在Spring的MultipartResolver之后执行。可以通过@Order注解设置优先级,比如@Order(Ordered.LOWEST_PRECEDENCE - 10),保证Multipart请求先被解析再进入日志拦截器。

内容的提问来源于stack exchange,提问作者HittingMyCode

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 08:10:00