From 7c47b470ff53f4e1950ea5a3bb4a6d0aa9654d07 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Mon, 6 Jun 2022 17:04:07 +0100 Subject: [PATCH] MockMvc allows HttpMethod input for multipart request Closes gh-28545 --- .../servlet/client/MockMvcHttpConnector.java | 4 +- ...ockMultipartHttpServletRequestBuilder.java | 20 +++++---- .../request/MockMvcRequestBuilders.java | 15 ++++++- .../standalone/MultipartControllerTests.java | 42 ++++++++++++++----- 4 files changed, 59 insertions(+), 22 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcHttpConnector.java b/spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcHttpConnector.java index ba6b040b10ea..b4d07e814c78 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcHttpConnector.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcHttpConnector.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -152,7 +152,7 @@ private MockHttpServletRequestBuilder initRequestBuilder( } // Parse the multipart request in order to adapt to Servlet Part's - MockMultipartHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.multipart(uri); + MockMultipartHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.multipart(httpMethod, uri); Assert.notNull(bytes, "No multipart content"); ReactiveHttpInputMessage inputMessage = MockServerHttpRequest.post(uri.toString()) 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 73a0a3bd29db..3f2b59909b83 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -69,12 +69,8 @@ public class MockMultipartHttpServletRequestBuilder extends MockHttpServletReque } /** - * Package-private constructor. Use static factory methods in - * {@link MockMvcRequestBuilders}. - *

For other ways to initialize a {@code MockMultipartHttpServletRequest}, - * see {@link #with(RequestPostProcessor)} and the - * {@link RequestPostProcessor} extension point. - * @param uri the URL + * Variant of {@link #MockMultipartHttpServletRequestBuilder(String, Object...)} + * with a {@link URI}. * @since 4.0.3 */ MockMultipartHttpServletRequestBuilder(URI uri) { @@ -82,6 +78,16 @@ public class MockMultipartHttpServletRequestBuilder extends MockHttpServletReque super.contentType(MediaType.MULTIPART_FORM_DATA); } + /** + * Variant of {@link #MockMultipartHttpServletRequestBuilder(String, Object...)} + * with a {@link URI} and an {@link HttpMethod}. + * @since 5.3.21 + */ + MockMultipartHttpServletRequestBuilder(HttpMethod httpMethod, URI uri) { + super(httpMethod, uri); + super.contentType(MediaType.MULTIPART_FORM_DATA); + } + /** * Create a new MockMultipartFile with the given content. 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 068320216f64..7334028daa72 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -215,7 +215,7 @@ public static MockMultipartHttpServletRequestBuilder multipart(String urlTemplat } /** - * Create a {@link MockMultipartHttpServletRequestBuilder} for a multipart request. + * Variant of {@link #multipart(String, Object...)} with a {@link URI}. * @param uri the URL * @since 5.0 */ @@ -223,6 +223,17 @@ public static MockMultipartHttpServletRequestBuilder multipart(URI uri) { return new MockMultipartHttpServletRequestBuilder(uri); } + /** + * Variant of {@link #multipart(String, Object...)} with a {@link URI} and + * an {@link HttpMethod}. + * @param httpMethod the HTTP method to use + * @param uri the URL + * @since 5.3.21 + */ + public static MockMultipartHttpServletRequestBuilder multipart(HttpMethod httpMethod, URI uri) { + return new MockMultipartHttpServletRequestBuilder(httpMethod, uri); + } + /** * Create a {@link MockMultipartHttpServletRequestBuilder} for a multipart request. * @param urlTemplate a URL template; the resulting URL will be encoded diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/client/standalone/MultipartControllerTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/client/standalone/MultipartControllerTests.java index b60a0e6f6e37..495ff3e17abc 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/client/standalone/MultipartControllerTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/client/standalone/MultipartControllerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,8 +39,8 @@ import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.test.web.servlet.client.MockMvcWebTestClient; import org.springframework.ui.Model; -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; @@ -80,6 +80,18 @@ public void multipartRequestWithSingleFile() throws Exception { MockMvcWebTestClient.resultActionsFor(exchangeResult) .andExpect(model().attribute("fileContent", fileContent)) .andExpect(model().attribute("jsonContent", json)); + + // Now try the same with HTTP PUT + exchangeResult = testClient.put().uri("/multipartfile-via-put") + .bodyValue(bodyBuilder.build()) + .exchange() + .expectStatus().isFound() + .expectBody().isEmpty(); + + // Further assertions on the server response + MockMvcWebTestClient.resultActionsFor(exchangeResult) + .andExpect(model().attribute("fileContent", fileContent)) + .andExpect(model().attribute("jsonContent", json)); } @Test @@ -284,10 +296,11 @@ public void multipartRequestWrapped() throws Exception { } + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @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 { @@ -301,7 +314,14 @@ public String processMultipartFile(@RequestParam(required = false) MultipartFile 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 { @@ -317,7 +337,7 @@ public String processMultipartFileArray(@RequestParam(required = false) Multipar 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 { @@ -333,7 +353,7 @@ public String processMultipartFileList(@RequestParam(required = false) List file, @RequestPart Map json, Model model) throws IOException { @@ -345,7 +365,7 @@ public String processOptionalFile(@RequestParam Optional file, return "redirect:/index"; } - @RequestMapping(value = "/optionalfilearray", method = RequestMethod.POST) + @PostMapping("/optionalfilearray") public String processOptionalFileArray(@RequestParam Optional file, @RequestPart Map json, Model model) throws IOException { @@ -359,7 +379,7 @@ public String processOptionalFileArray(@RequestParam Optional f return "redirect:/index"; } - @RequestMapping(value = "/optionalfilelist", method = RequestMethod.POST) + @PostMapping("/optionalfilelist") public String processOptionalFileList(@RequestParam Optional> file, @RequestPart Map json, Model model) throws IOException { @@ -373,7 +393,7 @@ public String processOptionalFileList(@RequestParam Optional return "redirect:/index"; } - @RequestMapping(value = "/part", method = RequestMethod.POST) + @PostMapping("/part") public String processPart(@RequestParam Part part, @RequestPart Map json, Model model) throws IOException { @@ -383,7 +403,7 @@ public String processPart(@RequestParam Part part, 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";