Affects version: 5.3.3 Related to: #26261
I have a controller method that accepts multipart/form-data
requests with 2 parameters: a file and a @RequestPart String label
.
Previously in my tests I was creating a MockPart
for the label and it was working fine: new MockPart("label", "mainImage".getBytes(UTF_8))
.
With the latest changes in 5.3.3 the MockPart
is converted to a request parameter instead of a multipart boundary and I don't receive anything for the RequestPart
in the controller thus breaking the tests.
Why is it necessary to convert MockParts to parameters in a multipart request?
I'm not sure if this is strictly related to the value being a parameter of if there is another underlying issue. I thought that I should receive the value by using either RequestPart
or RequestParam
just using different converters.
Comment From: rstoyanchev
This is likely a duplicate of #26400. Could you check if it works 5.3.4-SNAPSHOT?
Comment From: LickIt
Yes, I already tried running it with spring-test:5.3.4-SNAPSHOT but the result is still a "null" value in the controller.
Comment From: LickIt
I extracted a simple snippet for testing: https://github.com/LickIt/spring-mockpart-issue The test is passing with spring-test 5.3.2 but not with 5.3.3 and 5.3.4-SNAPSHOT
@Test
public void testUpload() throws Exception {
MockMultipartFile file = new MockMultipartFile("file", "file.png", "application/octet-stream", new byte[0]);
MockPart label = new MockPart("label", "mainImage".getBytes(UTF_8));
mockMvc.perform(multipart("/test").file(file).part(label))
.andExpect(content().string("mainImage"));
}
Here is the controller method:
@PostMapping(path = "/test", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String uploadFile(@RequestParam("file") MultipartFile file,
@RequestPart(value = "label", required = false) String label) {
return label;
}
Comment From: rstoyanchev
Thanks for the sample.
The latest code adds both a Part
and a request parameter, this is not the issue. The root cause appears to be in the way StandardMultipartHttpServletRequest
and MockMultipartHttpServletRequest
implement getMultipartHeaders(String)
. The former always returns headers (possibly empty) for a part that is present. The latter returns null if the part doesn't have a Content-Type
. Then RequestPartServletServerHttpRequest
takes this to mean there is no such part at all. In effect, currently, a MockPart
has to have a Content-Type to work with @RequestPart
. The issue is temporarily masked in 5.3.2 due to the use of StandardMultipartHttpServletRequest
but in previous versions, including 5.2.x the same issue is present.
From MultipartHttpServletRequest#getMultipartHeaders
:
* Return the headers associated with the specified part of the multipart request.
* <p>If the underlying implementation supports access to headers, then all headers are returned.
* Otherwise, the returned headers will include a 'Content-Type' header at the very least.
From RFC 7578 says:
Each part MAY have an (optional) "Content-Type" header field, which
defaults to "text/plain". If the contents of a file are to be sent,
the file data SHOULD be labeled with an appropriate media type, if
known, or "application/octet-stream".
I think the Javadoc for MultipartHttpServletRequest#getMultipartHeaders
is too strict since a Content-Type may legitimately not be present. We can align MockMultipartHttpServletRequest
to work like StandardMultipartHttpServletRequest
by returning the available headers, possibly empty. That has a better chance of decoding correctly based on the target Class
.
What do you think @jhoeller ?
Comment From: rstoyanchev
This should be fixed in the latest snapshots. If you could, please give it another try.
Comment From: LickIt
@rstoyanchev Thank you for the quick response and resolution! I can confirm this is fixed in the latest snapshot.
Comment From: rstoyanchev
Thanks for checking.