Spring 启动 2.5.x:所需的请求部分 'file' 不存在
Spring Boot 2.5.x: Required request part 'file' is not present
我有一个文件正在上传 api,它在 spring boot
版本 2.1.13
下工作得很好。升级版本到2.5.2
后,开始报异常。查看更改日志,我看不到任何与 Multipart
处理相关的重大更改。我能在这里错过什么?下面是我的示例代码。
异常
org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present
at org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver.resolveArgument(RequestPartMethodArgumentResolver.java:161) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121) ~[spring-web-5.3.8.jar:5.3.8]
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:170) ~[spring-web-5.3.8.jar:5.3.8]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137) ~[spring-web-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1063) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) [spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) [spring-webmvc-5.3.8.jar:5.3.8]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:681) [tomcat-embed-core-9.0.48.jar:4.0.FR]
application.properties
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=20MB
spring.servlet.multipart.max-request-size=20MB
控制器端点
@PostMapping(
value = "/upload",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<Object> uploadFile(@RequestPart("file") MultipartFile file) {
...
}
请求负载样本
POST http://localhost:8080/upload
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryGG9dgUb5THDV0eDB
------WebKitFormBoundaryGG9dgUb5THDV0eDB
Content-Disposition: form-data; name="file"; filename="Sample.pdf"
Content-Type: application/pdf
------WebKitFormBoundaryGG9dgUb5THDV0eDB--
注意: 我的配置中没有定义任何 MultipartResolver
bean。我尝试按如下方式添加 MultipartResolver bean 定义(一次仅添加一个),但似乎没有解决问题。
@Bean
public CommonsMultipartResolver multipartResolver() { // didn't work
return new CommonsMultipartResolver();
}
@Bean
public StandardServletMultipartResolver multipartResolver() { // didn't work
return new StandardServletMultipartResolver();
}
原来这个问题是在 Spring Boot 2.2
之后受到影响的。从那个版本开始,过滤器 HttpHiddenMethodFilter
默认被禁用。在 application.properties
.
中启用过滤器后问题得到解决
spring.mvc.hiddenmethod.filter.enabled=true
我的详细发现
上述过滤器的用途与我遇到的错误无关。但是作为执行过滤器的副作用,请求 parts
正在初始化。更具体地说,当过滤器尝试检索 _method
参数值 (e.g. request.getParameter("_method")
时,request
实例的 getParameter
方法在内部似乎解析参数,然后初始化请求 parts
。所以当过滤器在spring-boot
2.2 版本中被禁用时,没有任何东西可以初始化请求parts
。
我觉得请求 parts
初始化应该在 Spring
框架本身内修复。但在那之前,要么我们可以启用 HttpHiddenMethodFilter
过滤器,要么我们可以定义一个自定义过滤器来负责初始化请求 parts
,如下所示:
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
public class RequestInitializerFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
request.getParameterNames(); // this takes care of initializing request `parts`
filterChain.doFilter(request, response);
}
}
@RequestPart
默认为 required=true
(查看文档)。似乎 HttpHiddenMethodFilter
将值初始化为副作用。 spring 更新改变了这种行为。
要解决此问题,请设置 required=false
并在必要时初始化该值。
我有一个文件正在上传 api,它在 spring boot
版本 2.1.13
下工作得很好。升级版本到2.5.2
后,开始报异常。查看更改日志,我看不到任何与 Multipart
处理相关的重大更改。我能在这里错过什么?下面是我的示例代码。
异常
org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present
at org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver.resolveArgument(RequestPartMethodArgumentResolver.java:161) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121) ~[spring-web-5.3.8.jar:5.3.8]
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:170) ~[spring-web-5.3.8.jar:5.3.8]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137) ~[spring-web-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1063) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) [spring-webmvc-5.3.8.jar:5.3.8]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) [spring-webmvc-5.3.8.jar:5.3.8]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:681) [tomcat-embed-core-9.0.48.jar:4.0.FR]
application.properties
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=20MB
spring.servlet.multipart.max-request-size=20MB
控制器端点
@PostMapping(
value = "/upload",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<Object> uploadFile(@RequestPart("file") MultipartFile file) {
...
}
请求负载样本
POST http://localhost:8080/upload
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryGG9dgUb5THDV0eDB
------WebKitFormBoundaryGG9dgUb5THDV0eDB
Content-Disposition: form-data; name="file"; filename="Sample.pdf"
Content-Type: application/pdf
------WebKitFormBoundaryGG9dgUb5THDV0eDB--
注意: 我的配置中没有定义任何 MultipartResolver
bean。我尝试按如下方式添加 MultipartResolver bean 定义(一次仅添加一个),但似乎没有解决问题。
@Bean
public CommonsMultipartResolver multipartResolver() { // didn't work
return new CommonsMultipartResolver();
}
@Bean
public StandardServletMultipartResolver multipartResolver() { // didn't work
return new StandardServletMultipartResolver();
}
原来这个问题是在 Spring Boot 2.2
之后受到影响的。从那个版本开始,过滤器 HttpHiddenMethodFilter
默认被禁用。在 application.properties
.
spring.mvc.hiddenmethod.filter.enabled=true
我的详细发现
上述过滤器的用途与我遇到的错误无关。但是作为执行过滤器的副作用,请求 parts
正在初始化。更具体地说,当过滤器尝试检索 _method
参数值 (e.g. request.getParameter("_method")
时,request
实例的 getParameter
方法在内部似乎解析参数,然后初始化请求 parts
。所以当过滤器在spring-boot
2.2 版本中被禁用时,没有任何东西可以初始化请求parts
。
我觉得请求 parts
初始化应该在 Spring
框架本身内修复。但在那之前,要么我们可以启用 HttpHiddenMethodFilter
过滤器,要么我们可以定义一个自定义过滤器来负责初始化请求 parts
,如下所示:
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
public class RequestInitializerFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
request.getParameterNames(); // this takes care of initializing request `parts`
filterChain.doFilter(request, response);
}
}
@RequestPart
默认为 required=true
(查看文档)。似乎 HttpHiddenMethodFilter
将值初始化为副作用。 spring 更新改变了这种行为。
要解决此问题,请设置 required=false
并在必要时初始化该值。