Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve tests and Javadoc on binding to a property of type javax.servlet.Part #27830

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -38,6 +38,9 @@
import org.springframework.stereotype.Controller;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.ui.Model;
import org.springframework.util.StreamUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
Expand All @@ -55,6 +58,7 @@
/**
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @author Jaebin Joo
*/
public class MultipartControllerTests {

Expand Down Expand Up @@ -224,7 +228,7 @@ public void multipartRequestWithOptionalFileListNotPresent() throws Exception {
}

@Test
public void multipartRequestWithServletParts() throws Exception {
public void multipartRequestWithParts_resolvesMultipartFileArguments() throws Exception {
byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8);
MockPart filePart = new MockPart("file", "orig", fileContent);

Expand All @@ -239,6 +243,50 @@ public void multipartRequestWithServletParts() throws Exception {
.andExpect(model().attribute("jsonContent", Collections.singletonMap("name", "yeeeah")));
}

@Test
public void multipartRequestWithParts_resolvesPartArguments() throws Exception {
byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8);
MockPart filePart = new MockPart("file", "orig", fileContent);

byte[] json = "{\"name\":\"yeeeah\"}".getBytes(StandardCharsets.UTF_8);
MockPart jsonPart = new MockPart("json", json);
jsonPart.getHeaders().setContentType(MediaType.APPLICATION_JSON);

standaloneSetup(new MultipartController()).build()
.perform(multipart("/part").part(filePart).part(jsonPart))
.andExpect(status().isFound())
.andExpect(model().attribute("fileContent", fileContent))
.andExpect(model().attribute("jsonContent", Collections.singletonMap("name", "yeeeah")));
}

@Test
public void multipartRequestWithParts_resolvesMultipartFileProperties() throws Exception {
byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8);
MockPart filePart = new MockPart("file", "orig", fileContent);

standaloneSetup(new MultipartController()).build()
.perform(multipart("/multipartfileproperty").part(filePart))
.andExpect(status().isFound())
.andExpect(model().attribute("fileContent", fileContent));
}

@Test
public void multipartRequestWithParts_cannotResolvePartProperties() throws Exception {
byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8);
MockPart filePart = new MockPart("file", "orig", fileContent);

Exception exception = standaloneSetup(new MultipartController()).build()
.perform(multipart("/partproperty").part(filePart))
.andExpect(status().is4xxClientError())
.andReturn()
.getResolvedException();

assertThat(exception).isNotNull();
assertThat(exception).isInstanceOf(BindException.class);
assertThat(((BindException) exception).getFieldError("file"))
.as("MultipartRequest would not bind Part properties.").isNotNull();
}

@Test // SPR-13317
public void multipartRequestWrapped() throws Exception {
byte[] json = "{\"name\":\"yeeeah\"}".getBytes(StandardCharsets.UTF_8);
Expand Down Expand Up @@ -342,10 +390,13 @@ public String processOptionalFileList(@RequestParam Optional<List<MultipartFile>
}

@RequestMapping(value = "/part", method = RequestMethod.POST)
public String processPart(@RequestParam Part part,
public String processPart(@RequestPart Part file,
@RequestPart Map<String, String> json, Model model) throws IOException {

model.addAttribute("fileContent", part.getInputStream());
if (file != null) {
byte[] content = StreamUtils.copyToByteArray(file.getInputStream());
model.addAttribute("fileContent", content);
}
model.addAttribute("jsonContent", json);

return "redirect:/index";
Expand All @@ -356,8 +407,60 @@ public String processMultipart(@RequestPart Map<String, String> json, Model mode
model.addAttribute("json", json);
return "redirect:/index";
}

@RequestMapping(value = "/multipartfileproperty", method = RequestMethod.POST)
public String processMultipartFileBean(MultipartFileBean multipartFileBean, Model model, BindingResult bindingResult)
throws IOException {

if (!bindingResult.hasErrors()) {
MultipartFile file = multipartFileBean.getFile();
if (file != null) {
model.addAttribute("fileContent", file.getBytes());
}
}
return "redirect:/index";
}

@RequestMapping(value = "/partproperty", method = RequestMethod.POST)
public String processPartBean(PartBean partBean, Model model, BindingResult bindingResult)
throws IOException {

if (!bindingResult.hasErrors()) {
Part file = partBean.getFile();
if (file != null) {
byte[] content = StreamUtils.copyToByteArray(file.getInputStream());
model.addAttribute("fileContent", content);
}
}
return "redirect:/index";
}
}

private static class MultipartFileBean {

private MultipartFile file;

public MultipartFile getFile() {
return file;
}

public void setFile(MultipartFile file) {
this.file = file;
}
}

private static class PartBean {

private Part file;

public Part getFile() {
return file;
}

public void setFile(Part file) {
this.file = file;
}
}

private static class RequestWrappingFilter extends OncePerRequestFilter {

Expand Down
Expand Up @@ -95,11 +95,14 @@ public ServletRequestDataBinder(@Nullable Object target, String objectName) {
* HTTP parameters: i.e. "uploadedFile" to an "uploadedFile" bean property,
* invoking a "setUploadedFile" setter method.
* <p>The type of the target property for a multipart file can be MultipartFile,
* byte[], or String. The latter two receive the contents of the uploaded file;
* Part, byte[], or String. The Part binding is only supported when the request
* is not a MultipartRequest. The latter two receive the contents of the uploaded file;
* all metadata like original file name, content type, etc are lost in those cases.
* @param request the request with parameters to bind (can be multipart)
* @see org.springframework.web.multipart.MultipartHttpServletRequest
* @see org.springframework.web.multipart.MultipartRequest
* @see org.springframework.web.multipart.MultipartFile
* @see jakarta.servlet.http.Part
* @see #bind(org.springframework.beans.PropertyValues)
*/
public void bind(ServletRequest request) {
Expand Down
Expand Up @@ -99,7 +99,8 @@ public WebRequestDataBinder(@Nullable Object target, String objectName) {
* HTTP parameters: i.e. "uploadedFile" to an "uploadedFile" bean property,
* invoking a "setUploadedFile" setter method.
* <p>The type of the target property for a multipart file can be Part, MultipartFile,
* byte[], or String. The latter two receive the contents of the uploaded file;
* byte[], or String. The Part binding is only supported when the request
* is not a MultipartRequest. The latter two receive the contents of the uploaded file;
* all metadata like original file name, content type, etc are lost in those cases.
* @param request the request with parameters to bind (can be multipart)
* @see org.springframework.web.multipart.MultipartRequest
Expand Down