Spring自定义DispatcherServlet记录multipart请求报错如何解决
问题原因
核心错误有三点:
- 逻辑错误:判断为multipart请求时你主动跳过了
super.doDispatch(request, response)调用,导致请求没有被Spring MVC分发到对应控制器,自然没有响应生成,后续updateResponse操作必然失败 - 配置缺失:你自定义注册的DispatcherServlet没有绑定Spring Boot自动生成的multipart配置,所以解析multipart请求时会抛出
Unable to process parts as no multi-part configuration has been provided错误 - 请求包装错误:你直接用
ContentCachingRequestWrapper包装了原始multipart请求,提前消费了请求输入流,导致Spring的MultipartResolver无法正常解析文件参数
解决方案
分两步修改代码:
1. 修正Servlet注册配置,绑定multipart配置
修改你的LogsConfig类,注入Spring Boot自动配置的MultipartConfigElement并绑定到ServletRegistrationBean:
import com.altair.autoTester.logs.LoggableDispatcherServlet; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.DispatcherServlet; import javax.servlet.MultipartConfigElement; @Configuration public class LogsConfig { // 注入Spring Boot自动配置好的multipart参数 @Autowired private MultipartConfigElement multipartConfig; @Bean public ServletRegistrationBean dispatcherRegistration() { ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet()); // 绑定multipart配置到自定义DispatcherServlet registration.setMultipartConfig(multipartConfig); return registration; } @Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServlet dispatcherServlet() { return new LoggableDispatcherServlet(); } }
2. 修正LoggableDispatcherServlet的分发逻辑
删除跳过super.doDispatch的逻辑,multipart请求不做RequestWrapper包装,避免破坏请求解析:
@Override protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { boolean isMultipart = request.getContentType() != null && request.getContentType().toLowerCase().contains("multipart/form-data"); HttpServletRequest processedRequest = request; // 非multipart请求才用ContentCachingRequestWrapper包装,避免干扰文件解析 if (!isMultipart && !(request instanceof ContentCachingRequestWrapper)) { processedRequest = new ContentCachingRequestWrapper(request); } if (!(response instanceof ContentCachingResponseWrapper)) { response = new ContentCachingResponseWrapper(response); } HandlerExecutionChain handler = getHandler(processedRequest); try { // 所有类型的请求都必须调用父类分发逻辑才能进入控制器 super.doDispatch(processedRequest, response); } catch (Exception e) { logger.error(e.getMessage(), e); // 务必抛出异常,否则Spring全局异常处理器不会生效 throw e; } finally { // 统一执行日志和响应更新逻辑 log(processedRequest, response, handler); updateResponse(response); } }
补充说明
- multipart请求的普通表单参数可以直接通过
request.getParameterMap()获取,不需要读取请求体打日志 - 不建议读取上传的二进制文件内容打日志,会额外占用大量内存和IO资源
- 如果你确实需要记录上传文件的元信息(如文件名、大小),可以从解析后的
MultipartHttpServletRequest中直接获取,不需要操作原始请求流
内容的提问来源于stack exchange,提问作者Gilad Dahan
相关产品推荐
相关产品推荐

