Spring Boot 2.5.x: Required request part 'file' is not present
Asked Answered
M

4

6

I have a file uploading api which was working perfectly fine under the spring boot version 2.1.13. After upgrading the version to 2.5.2, it started to throw an exception. Looking at the changelogs, I couldn't see anything significant changes that's related to Multipart processing. What could I be missing here? Below are the sample codes I have.

Exception

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

controller end point

@PostMapping(
    value = "/upload", 
    consumes = MediaType.MULTIPART_FORM_DATA_VALUE, 
    produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<Object> uploadFile(@RequestPart("file") MultipartFile file) {
...
}

Request Payload Sample

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--

Note: I don't have any MultipartResolver bean defined in my configuration. I tried adding the MultipartResolver bean definition as follows (only one at a time) but didn't seem to resolve the issue.

@Bean
public CommonsMultipartResolver multipartResolver() { // didn't work
    return new CommonsMultipartResolver();
}

@Bean
public StandardServletMultipartResolver multipartResolver() { // didn't work
    return new StandardServletMultipartResolver();
}
Marilynnmarimba answered 13/8, 2021 at 1:49 Comment(0)
M
11

It turns out this issue was affected after the Spring Boot 2.2. Since that version, the filter HttpHiddenMethodFilter was disabled by default. The issue got fixed after enabling the filter in application.properties.

spring.mvc.hiddenmethod.filter.enabled=true

My Findings in Detail

The purpose of the above filter has nothing to do with the error I was getting. But the request parts was getting initialized as a side effect of executing the filter. More specifically, when the filter tries to retrieve the _method parameter value (e.g. request.getParameter("_method"), the getParameter method of the request instance internally seems to parse the parameters which then initializes the request parts. So when the filter was disabled in spring-boot version 2.2, there was nothing to initialize the request parts.

I feel like the request parts initialization should be fixed within the Spring framework itself. But until then, either we could enable the HttpHiddenMethodFilter filter, or we could define a custom filter that takes care of initializing the request parts, something like below:

@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);
    }
}
Marilynnmarimba answered 17/8, 2021 at 16:46 Comment(0)
C
1

@RequestPart has required=true as default (take a look to the documentation). It seems that the HttpHiddenMethodFilter initialize the value as a sideeffect. The spring update change this behavior. To fix this, set required=false and initialize the value if necessary.

Coseismal answered 12/1, 2022 at 8:16 Comment(0)
O
0

Thanks @ManojShrestha's comment. I don't know why but in my case, I don't need to config hiddenmethod in application.properties. I just add custom filter class. But anyway, it worked.

Osteitis answered 23/8, 2023 at 2:54 Comment(0)
W
0

@RequestPart(value = "images", required = false) List images
try this it worked for me

Westbrooke answered 11/9 at 4:32 Comment(1)
This does not really answer the question. If you have a different question, you can ask it by clicking Ask Question. To get notified when this question gets new answers, you can follow this question. Once you have enough reputation, you can also add a bounty to draw more attention to this question. - From ReviewInevitable

© 2022 - 2024 — McMap. All rights reserved.