We are currently developing a Spring MVC environment using functional routing like this:

router {
    POST("/user-photo/", userImageHandler::create)
}

@Component("user.Image")
class UserImageHandler : PassHandler() {

    fun create(serverRequest: ServerRequest) = success {
        val multipartRequest = (serverRequest.servletRequest() as? MultipartHttpServletRequest) ?:
             throw ResponseStatusException(
                 HttpStatus.NOT_ACCEPTABLE,
                 "Request is not a multipart request"
              )
        val file: MultipartFile = multipartRequest.getFile("file") ?: throw ResponseStatusException(
             HttpStatus.NOT_ACCEPTABLE,
             "Missing file with name: file"
        )
     ....
    }
}

The ServerRequest is the https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/function/ServerRequest.html interface. The only way to access files seem to be the casting method we do right now - serverRequest.servletRequest().parts is empty.

This is the integration test:

        val file = MockMultipartFile(
            "blank.jpg",
            "blank.jpg",
            "image/jpg",
            getImageBytes()
        )

        mockMvc.perform(
            MockMvcRequestBuilders.multipart("/user-photo/")
                .file(file)
                .accept(MediaType.APPLICATION_JSON)
        ).andExpect(
            MockMvcResultMatchers.status().isOk
        )

In the integration test the serverRequest.servletRequest part is of type SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequest@71f08480] and thus not a decendant of MultipartHttpServletRequest.

Is there a smarter way to access multipart files with the ServerRequest interface? Otherwise I would think that HttpServletRequest is missing a getFiles() method.

I found an way to access multipart files but it's a few levels deep and would require a lot of casting: Spring Expose multipart data in ServerRequest

Comment From: poutsma

We're going to add a method that exposes the parts in 5.3, but in the mean time you should be able to get them via HttpServletRequest::getParts, which was introduced in Servlet 3.0.

Comment From: jonasbark

@poutsma thank you, sounds good! Regarding your getParts reference - I already checked this and mentioned it in the initial post as well:

serverRequest.servletRequest().parts is empty. So no luck with getParts as well it seems.

If it helps I can build a minimal project.

Comment From: poutsma

That's strange. Both SecurityContextHolderAwareRequestWrapper and HttpWriterFilter.HeaderWriterRequest extend Servlet's HttpServletRequestWrapper, so they should just pass the parts through.

I am not able to reproduce this behavior with this simple route:

@Bean
public RouterFunction<ServerResponse> route() {
    return RouterFunctions.route()
            .POST("/", request -> {
                for (Part part : request.servletRequest().getParts()) {
                    System.out.println(part.getName() + " " + part.getSize());
                }
                return ServerResponse.ok().build();
            })
            .build();
}

when invoking the above with curl -F, the part names and sizes are printed as expected.

So please, do provide a minimal project that reproduces this behavior.

Comment From: jonasbark

I tested it with a real API request as well and it works. But what I'm really trying to achieve here is to get the integration test to run with MockMvc. I posted the integration test content in the initial post.

Spring Expose multipart data in ServerRequest

 @Test
    fun `test create`(): Unit = runBlocking {
        val file = MockMultipartFile(
            "blank.jpg",
            "blank.jpg",
            "image/jpg",
            getImageBytes()
        )

        mockMvc.perform(
            MockMvcRequestBuilders.multipart("/user-photo/")
                .file(file)
                .accept(MediaType.APPLICATION_JSON)
        ).andExpect(
            MockMvcResultMatchers.status().isOk
        )
        Unit
    }

I created a fairly minimal repo here: https://github.com/jonasbark/spring-framework-24909

Comment From: poutsma

As it turned out, the solution was to use part (and MockPart) on the builder, instead of file. MultipartFile is a Spring abstraction introduced before Servlet offered Part. As such, the HttpServletRequest does not "know" about MultipartFile objects, only Part.

This test works for me:

@Test
fun `test create`(): Unit = runBlocking {
    val part = MockPart(
        "blank.jpg",
        "blank.jpg",
        getImageBytes()
    )

    mockMvc.perform(
        MockMvcRequestBuilders.multipart("/user-photo/")
            .part(part)
            .accept(MediaType.APPLICATION_JSON)
    ).andExpect(
        MockMvcResultMatchers.status().isOk
    )
    Unit
}

Though your initial problem is fixed, I am going to keep this issue around as a feature request to expose multipart data on WebMvn.fn's ServerRequest directly, without resorting to HttpServletRequest.