Manoj Shrestha
Manoj Shrestha

Reputation: 4684

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

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();
}

Upvotes: 6

Views: 11031

Answers (4)

divya
divya

Reputation: 1

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

Upvotes: 0

ginbarca
ginbarca

Reputation: 31

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.

Upvotes: 0

Dennis
Dennis

Reputation: 11

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

Upvotes: 1

Manoj Shrestha
Manoj Shrestha

Reputation: 4684

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);
    }
}

Upvotes: 12

Related Questions