Spring Boot日志拦截器引发Multipart请求@RequestPart丢失问题
解决Spring Boot日志拦截器导致Multipart请求@RequestPart丢失的问题
问题根源在于你自定义的CachedHttpServletRequest没有处理Multipart请求的特殊逻辑:Spring在解析multipart/form-data请求后,会将原始HttpServletRequest包装为MultipartHttpServletRequest,而你的缓存类没有实现这个接口,也没有复用原始Multipart请求的part信息,导致后续控制器无法获取到@RequestPart指定的文件。
解决方案步骤:
让缓存类实现
MultipartHttpServletRequest接口
扩展你的CachedHttpServletRequest,实现Spring的MultipartHttpServletRequest接口,将Multipart相关的方法委托给原始的Multipart请求实例。区分请求类型进行包装
在拦截器中判断原始请求是否为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
相关产品推荐
相关产品推荐

