diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilder.java index 87250bae7a..6e3fd56b0b 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilder.java @@ -64,7 +64,16 @@ public class MockMultipartHttpServletRequestBuilder extends MockHttpServletReque * @param uriVariables zero or more URI variables */ MockMultipartHttpServletRequestBuilder(String urlTemplate, Object... uriVariables) { - super(HttpMethod.POST, urlTemplate, uriVariables); + this(HttpMethod.POST, urlTemplate, uriVariables); + } + + /** + * Variant of {@link #MockMultipartHttpServletRequestBuilder(String, Object...)} + * that also accepts an {@link HttpMethod}. + * @since 5.3.22 + */ + MockMultipartHttpServletRequestBuilder(HttpMethod httpMethod, String urlTemplate, Object... uriVariables) { + super(httpMethod, urlTemplate, uriVariables); super.contentType(MediaType.MULTIPART_FORM_DATA); } @@ -74,8 +83,7 @@ public class MockMultipartHttpServletRequestBuilder extends MockHttpServletReque * @since 4.0.3 */ MockMultipartHttpServletRequestBuilder(URI uri) { - super(HttpMethod.POST, uri); - super.contentType(MediaType.MULTIPART_FORM_DATA); + this(HttpMethod.POST, uri); } /** diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java index d92f610955..077a93d92a 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java @@ -50,10 +50,10 @@ public abstract class MockMvcRequestBuilders { /** * Create a {@link MockHttpServletRequestBuilder} for a GET request. * @param urlTemplate a URL template; the resulting URL will be encoded - * @param uriVars zero or more URI variables + * @param uriVariables zero or more URI variables */ - public static MockHttpServletRequestBuilder get(String urlTemplate, Object... uriVars) { - return new MockHttpServletRequestBuilder(HttpMethod.GET, urlTemplate, uriVars); + public static MockHttpServletRequestBuilder get(String urlTemplate, Object... uriVariables) { + return new MockHttpServletRequestBuilder(HttpMethod.GET, urlTemplate, uriVariables); } /** @@ -68,10 +68,10 @@ public abstract class MockMvcRequestBuilders { /** * Create a {@link MockHttpServletRequestBuilder} for a POST request. * @param urlTemplate a URL template; the resulting URL will be encoded - * @param uriVars zero or more URI variables + * @param uriVariables zero or more URI variables */ - public static MockHttpServletRequestBuilder post(String urlTemplate, Object... uriVars) { - return new MockHttpServletRequestBuilder(HttpMethod.POST, urlTemplate, uriVars); + public static MockHttpServletRequestBuilder post(String urlTemplate, Object... uriVariables) { + return new MockHttpServletRequestBuilder(HttpMethod.POST, urlTemplate, uriVariables); } /** @@ -86,10 +86,10 @@ public abstract class MockMvcRequestBuilders { /** * Create a {@link MockHttpServletRequestBuilder} for a PUT request. * @param urlTemplate a URL template; the resulting URL will be encoded - * @param uriVars zero or more URI variables + * @param uriVariables zero or more URI variables */ - public static MockHttpServletRequestBuilder put(String urlTemplate, Object... uriVars) { - return new MockHttpServletRequestBuilder(HttpMethod.PUT, urlTemplate, uriVars); + public static MockHttpServletRequestBuilder put(String urlTemplate, Object... uriVariables) { + return new MockHttpServletRequestBuilder(HttpMethod.PUT, urlTemplate, uriVariables); } /** @@ -104,10 +104,10 @@ public abstract class MockMvcRequestBuilders { /** * Create a {@link MockHttpServletRequestBuilder} for a PATCH request. * @param urlTemplate a URL template; the resulting URL will be encoded - * @param uriVars zero or more URI variables + * @param uriVariables zero or more URI variables */ - public static MockHttpServletRequestBuilder patch(String urlTemplate, Object... uriVars) { - return new MockHttpServletRequestBuilder(HttpMethod.PATCH, urlTemplate, uriVars); + public static MockHttpServletRequestBuilder patch(String urlTemplate, Object... uriVariables) { + return new MockHttpServletRequestBuilder(HttpMethod.PATCH, urlTemplate, uriVariables); } /** @@ -122,10 +122,10 @@ public abstract class MockMvcRequestBuilders { /** * Create a {@link MockHttpServletRequestBuilder} for a DELETE request. * @param urlTemplate a URL template; the resulting URL will be encoded - * @param uriVars zero or more URI variables + * @param uriVariables zero or more URI variables */ - public static MockHttpServletRequestBuilder delete(String urlTemplate, Object... uriVars) { - return new MockHttpServletRequestBuilder(HttpMethod.DELETE, urlTemplate, uriVars); + public static MockHttpServletRequestBuilder delete(String urlTemplate, Object... uriVariables) { + return new MockHttpServletRequestBuilder(HttpMethod.DELETE, urlTemplate, uriVariables); } /** @@ -140,10 +140,10 @@ public abstract class MockMvcRequestBuilders { /** * Create a {@link MockHttpServletRequestBuilder} for an OPTIONS request. * @param urlTemplate a URL template; the resulting URL will be encoded - * @param uriVars zero or more URI variables + * @param uriVariables zero or more URI variables */ - public static MockHttpServletRequestBuilder options(String urlTemplate, Object... uriVars) { - return new MockHttpServletRequestBuilder(HttpMethod.OPTIONS, urlTemplate, uriVars); + public static MockHttpServletRequestBuilder options(String urlTemplate, Object... uriVariables) { + return new MockHttpServletRequestBuilder(HttpMethod.OPTIONS, urlTemplate, uriVariables); } /** @@ -158,11 +158,11 @@ public abstract class MockMvcRequestBuilders { /** * Create a {@link MockHttpServletRequestBuilder} for a HEAD request. * @param urlTemplate a URL template; the resulting URL will be encoded - * @param uriVars zero or more URI variables + * @param uriVariables zero or more URI variables * @since 4.1 */ - public static MockHttpServletRequestBuilder head(String urlTemplate, Object... uriVars) { - return new MockHttpServletRequestBuilder(HttpMethod.HEAD, urlTemplate, uriVars); + public static MockHttpServletRequestBuilder head(String urlTemplate, Object... uriVariables) { + return new MockHttpServletRequestBuilder(HttpMethod.HEAD, urlTemplate, uriVariables); } /** @@ -178,10 +178,10 @@ public abstract class MockMvcRequestBuilders { * Create a {@link MockHttpServletRequestBuilder} for a request with the given HTTP method. * @param method the HTTP method (GET, POST, etc) * @param urlTemplate a URL template; the resulting URL will be encoded - * @param uriVars zero or more URI variables + * @param uriVariables zero or more URI variables */ - public static MockHttpServletRequestBuilder request(HttpMethod method, String urlTemplate, Object... uriVars) { - return new MockHttpServletRequestBuilder(method, urlTemplate, uriVars); + public static MockHttpServletRequestBuilder request(HttpMethod method, String urlTemplate, Object... uriVariables) { + return new MockHttpServletRequestBuilder(method, urlTemplate, uriVariables); } /** @@ -205,13 +205,26 @@ public abstract class MockMvcRequestBuilders { } /** - * Create a {@link MockMultipartHttpServletRequestBuilder} for a multipart request. + * Create a {@link MockMultipartHttpServletRequestBuilder} for a multipart request, + * using POST as the HTTP method. * @param urlTemplate a URL template; the resulting URL will be encoded - * @param uriVars zero or more URI variables + * @param uriVariables zero or more URI variables * @since 5.0 */ - public static MockMultipartHttpServletRequestBuilder multipart(String urlTemplate, Object... uriVars) { - return new MockMultipartHttpServletRequestBuilder(urlTemplate, uriVars); + public static MockMultipartHttpServletRequestBuilder multipart(String urlTemplate, Object... uriVariables) { + return new MockMultipartHttpServletRequestBuilder(urlTemplate, uriVariables); + } + + /** + * Variant of {@link #multipart(String, Object...)} that also accepts an + * {@link HttpMethod}. + * @param httpMethod the HTTP method to use + * @param urlTemplate a URL template; the resulting URL will be encoded + * @param uriVariables zero or more URI variables + * @since 5.3.22 + */ + public static MockMultipartHttpServletRequestBuilder multipart(HttpMethod httpMethod, String urlTemplate, Object... uriVariables) { + return new MockMultipartHttpServletRequestBuilder(httpMethod, urlTemplate, uriVariables); } /** diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/MultipartControllerTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/MultipartControllerTests.java index 5d76a81574..cf9de28ef6 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/MultipartControllerTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/MultipartControllerTests.java @@ -34,6 +34,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.http.HttpMethod; import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockPart; import org.springframework.stereotype.Controller; @@ -42,8 +43,8 @@ import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequ import org.springframework.ui.Model; import org.springframework.util.StreamUtils; 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.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.filter.OncePerRequestFilter; @@ -60,20 +61,30 @@ import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standal * @author Rossen Stoyanchev * @author Juergen Hoeller * @author Jaebin Joo + * @author Sam Brannen */ -public class MultipartControllerTests { +class MultipartControllerTests { @ParameterizedTest - @ValueSource(strings = {"/multipartfile", "/part"}) - public void multipartRequestWithSingleFileOrPart(String url) throws Exception { + @ValueSource(strings = {"/multipartfile", "/multipartfile-via-put", "/part"}) + void multipartRequestWithSingleFileOrPart(String url) throws Exception { byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8); byte[] json = "{\"name\":\"yeeeah\"}".getBytes(StandardCharsets.UTF_8); MockMultipartFile jsonPart = new MockMultipartFile("json", "json", "application/json", json); - MockMultipartHttpServletRequestBuilder requestBuilder = (url.endsWith("file") ? - multipart(url).file(new MockMultipartFile("file", "orig", null, fileContent)) : - multipart(url).part(new MockPart("part", "orig", fileContent))); + MockMultipartHttpServletRequestBuilder requestBuilder; + switch (url) { + case "/multipartfile": + requestBuilder = multipart(url).file(new MockMultipartFile("file", "orig", null, fileContent)); + break; + case "/multipartfile-via-put": + requestBuilder = multipart(HttpMethod.PUT, url).file(new MockMultipartFile("file", "orig", null, fileContent)); + break; + default: + requestBuilder = multipart(url).part(new MockPart("part", "orig", fileContent)); + break; + } standaloneSetup(new MultipartController()).build() .perform(requestBuilder.file(jsonPart)) @@ -83,14 +94,14 @@ public class MultipartControllerTests { } @Test - public void multipartRequestWithSingleFileNotPresent() throws Exception { + void multipartRequestWithSingleFileNotPresent() throws Exception { standaloneSetup(new MultipartController()).build() .perform(multipart("/multipartfile")) .andExpect(status().isFound()); } @Test - public void multipartRequestWithFileArray() throws Exception { + void multipartRequestWithFileArray() throws Exception { byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8); MockMultipartFile filePart1 = new MockMultipartFile("file", "orig", null, fileContent); MockMultipartFile filePart2 = new MockMultipartFile("file", "orig", null, fileContent); @@ -106,21 +117,21 @@ public class MultipartControllerTests { } @Test - public void multipartRequestWithFileArrayNotPresent() throws Exception { + void multipartRequestWithFileArrayNotPresent() throws Exception { standaloneSetup(new MultipartController()).build() .perform(multipart("/multipartfilearray")) .andExpect(status().isFound()); } @Test - public void multipartRequestWithFileArrayNoMultipart() throws Exception { + void multipartRequestWithFileArrayNoMultipart() throws Exception { standaloneSetup(new MultipartController()).build() .perform(post("/multipartfilearray")) .andExpect(status().isFound()); } @Test - public void multipartRequestWithFileList() throws Exception { + void multipartRequestWithFileList() throws Exception { byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8); MockMultipartFile filePart1 = new MockMultipartFile("file", "orig", null, fileContent); MockMultipartFile filePart2 = new MockMultipartFile("file", "orig", null, fileContent); @@ -136,21 +147,21 @@ public class MultipartControllerTests { } @Test - public void multipartRequestWithFileListNotPresent() throws Exception { + void multipartRequestWithFileListNotPresent() throws Exception { standaloneSetup(new MultipartController()).build() .perform(multipart("/multipartfilelist")) .andExpect(status().isFound()); } @Test - public void multipartRequestWithFileListNoMultipart() throws Exception { + void multipartRequestWithFileListNoMultipart() throws Exception { standaloneSetup(new MultipartController()).build() .perform(post("/multipartfilelist")) .andExpect(status().isFound()); } @Test - public void multipartRequestWithOptionalFile() throws Exception { + void multipartRequestWithOptionalFile() throws Exception { byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8); MockMultipartFile filePart = new MockMultipartFile("file", "orig", null, fileContent); @@ -165,7 +176,7 @@ public class MultipartControllerTests { } @Test - public void multipartRequestWithOptionalFileNotPresent() throws Exception { + void multipartRequestWithOptionalFileNotPresent() throws Exception { byte[] json = "{\"name\":\"yeeeah\"}".getBytes(StandardCharsets.UTF_8); MockMultipartFile jsonPart = new MockMultipartFile("json", "json", "application/json", json); @@ -177,7 +188,7 @@ public class MultipartControllerTests { } @Test - public void multipartRequestWithOptionalFileArray() throws Exception { + void multipartRequestWithOptionalFileArray() throws Exception { byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8); MockMultipartFile filePart1 = new MockMultipartFile("file", "orig", null, fileContent); MockMultipartFile filePart2 = new MockMultipartFile("file", "orig", null, fileContent); @@ -193,7 +204,7 @@ public class MultipartControllerTests { } @Test - public void multipartRequestWithOptionalFileArrayNotPresent() throws Exception { + void multipartRequestWithOptionalFileArrayNotPresent() throws Exception { byte[] json = "{\"name\":\"yeeeah\"}".getBytes(StandardCharsets.UTF_8); MockMultipartFile jsonPart = new MockMultipartFile("json", "json", "application/json", json); @@ -205,7 +216,7 @@ public class MultipartControllerTests { } @Test - public void multipartRequestWithOptionalFileList() throws Exception { + void multipartRequestWithOptionalFileList() throws Exception { byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8); MockMultipartFile filePart1 = new MockMultipartFile("file", "orig", null, fileContent); MockMultipartFile filePart2 = new MockMultipartFile("file", "orig", null, fileContent); @@ -221,7 +232,7 @@ public class MultipartControllerTests { } @Test - public void multipartRequestWithOptionalFileListNotPresent() throws Exception { + void multipartRequestWithOptionalFileListNotPresent() throws Exception { byte[] json = "{\"name\":\"yeeeah\"}".getBytes(StandardCharsets.UTF_8); MockMultipartFile jsonPart = new MockMultipartFile("json", "json", "application/json", json); @@ -233,7 +244,7 @@ public class MultipartControllerTests { } @Test - public void multipartRequestWithDataBindingToFile() throws Exception { + void multipartRequestWithDataBindingToFile() throws Exception { byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8); MockPart filePart = new MockPart("file", "orig", fileContent); @@ -244,7 +255,7 @@ public class MultipartControllerTests { } @Test // SPR-13317 - public void multipartRequestWrapped() throws Exception { + void multipartRequestWrapped() throws Exception { byte[] json = "{\"name\":\"yeeeah\"}".getBytes(StandardCharsets.UTF_8); MockMultipartFile jsonPart = new MockMultipartFile("json", "json", "application/json", json); @@ -259,7 +270,7 @@ public class MultipartControllerTests { @Controller private static class MultipartController { - @RequestMapping(value = "/multipartfile", method = RequestMethod.POST) + @PostMapping("/multipartfile") public String processMultipartFile(@RequestParam(required = false) MultipartFile file, @RequestPart(required = false) Map json, Model model) throws IOException { @@ -273,7 +284,14 @@ public class MultipartControllerTests { return "redirect:/index"; } - @RequestMapping(value = "/multipartfilearray", method = RequestMethod.POST) + @PutMapping("/multipartfile-via-put") + public String processMultipartFileViaHttpPut(@RequestParam(required = false) MultipartFile file, + @RequestPart(required = false) Map json, Model model) throws IOException { + + return processMultipartFile(file, json, model); + } + + @PostMapping("/multipartfilearray") public String processMultipartFileArray(@RequestParam(required = false) MultipartFile[] file, @RequestPart(required = false) Map json, Model model) throws IOException { @@ -289,7 +307,7 @@ public class MultipartControllerTests { return "redirect:/index"; } - @RequestMapping(value = "/multipartfilelist", method = RequestMethod.POST) + @PostMapping("/multipartfilelist") public String processMultipartFileList(@RequestParam(required = false) List file, @RequestPart(required = false) Map json, Model model) throws IOException { @@ -305,7 +323,7 @@ public class MultipartControllerTests { return "redirect:/index"; } - @RequestMapping(value = "/optionalfile", method = RequestMethod.POST) + @PostMapping("/optionalfile") public String processOptionalFile(@RequestParam Optional file, @RequestPart Map json, Model model) throws IOException { @@ -317,7 +335,7 @@ public class MultipartControllerTests { return "redirect:/index"; } - @RequestMapping(value = "/optionalfilearray", method = RequestMethod.POST) + @PostMapping("/optionalfilearray") public String processOptionalFileArray(@RequestParam Optional file, @RequestPart Map json, Model model) throws IOException { @@ -331,7 +349,7 @@ public class MultipartControllerTests { return "redirect:/index"; } - @RequestMapping(value = "/optionalfilelist", method = RequestMethod.POST) + @PostMapping("/optionalfilelist") public String processOptionalFileList(@RequestParam Optional> file, @RequestPart Map json, Model model) throws IOException { @@ -345,7 +363,7 @@ public class MultipartControllerTests { return "redirect:/index"; } - @RequestMapping(value = "/part", method = RequestMethod.POST) + @PostMapping("/part") public String processPart(@RequestPart Part part, @RequestPart Map json, Model model) throws IOException { @@ -358,13 +376,13 @@ public class MultipartControllerTests { return "redirect:/index"; } - @RequestMapping(value = "/json", method = RequestMethod.POST) + @PostMapping("/json") public String processMultipart(@RequestPart Map json, Model model) { model.addAttribute("json", json); return "redirect:/index"; } - @RequestMapping(value = "/multipartfilebinding", method = RequestMethod.POST) + @PostMapping("/multipartfilebinding") public String processMultipartFileBean( MultipartFileBean multipartFileBean, Model model, BindingResult bindingResult) throws IOException {