diff --git a/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java b/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java index c32a7b73d5a..a2eb29a0101 100644 --- a/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java +++ b/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java @@ -19,6 +19,8 @@ package org.springframework.test.web.client.match; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; @@ -60,7 +62,12 @@ import static org.springframework.test.util.AssertionErrors.assertTrue; */ public class ContentRequestMatchers { - private static final String DEFAULT_ENCODING = "UTF-8"; + /** + * The encoding for parsing multipart content when the sender hasn't specified it. + * @see DiskFileItemFactory#setDefaultCharset(String) + */ + private static final Charset DEFAULT_MULTIPART_ENCODING = StandardCharsets.UTF_8; + private final XmlExpectationsHelper xmlHelper; @@ -206,7 +213,15 @@ public class ContentRequestMatchers { * @since 5.3 */ public RequestMatcher multipartData(MultiValueMap expectedMap) { - return multipartData(expectedMap, true); + return multipartData(expectedMap, DEFAULT_MULTIPART_ENCODING, true); + } + + /** + * Variant of {@link #multipartData(MultiValueMap)} with a defaultCharset. + * @since 6.2 + */ + public RequestMatcher multipartData(MultiValueMap expectedMap, Charset defaultCharset) { + return multipartData(expectedMap, defaultCharset, true); } /** @@ -221,13 +236,15 @@ public class ContentRequestMatchers { public RequestMatcher multipartDataContains(Map expectedMap) { MultiValueMap map = new LinkedMultiValueMap<>(expectedMap.size()); expectedMap.forEach(map::add); - return multipartData(map, false); + return multipartData(map, DEFAULT_MULTIPART_ENCODING, false); } @SuppressWarnings("ConstantConditions") - private RequestMatcher multipartData(MultiValueMap expectedMap, boolean containsExactly) { + private RequestMatcher multipartData( + MultiValueMap expectedMap, Charset defaultCharset, boolean containsExactly) { + return request -> { - MultiValueMap actualMap = MultipartHelper.parse((MockClientHttpRequest) request); + MultiValueMap actualMap = MultipartHelper.parse((MockClientHttpRequest) request, defaultCharset); if (containsExactly) { assertEquals("Multipart request content: " + actualMap, expectedMap.size(), actualMap.size()); } @@ -366,11 +383,11 @@ public class ContentRequestMatchers { private static class MultipartHelper { - public static MultiValueMap parse(MockClientHttpRequest request) { + public static MultiValueMap parse(MockClientHttpRequest request, Charset defaultCharset) { try { FileUpload fileUpload = new FileUpload(); DiskFileItemFactory factory = new DiskFileItemFactory(); - factory.setDefaultCharset(DEFAULT_ENCODING); + factory.setDefaultCharset(defaultCharset.name()); fileUpload.setFileItemFactory(factory); List fileItems = fileUpload.parseRequest(new UploadContext() { diff --git a/spring-test/src/test/java/org/springframework/test/web/client/match/ContentRequestMatchersTests.java b/spring-test/src/test/java/org/springframework/test/web/client/match/ContentRequestMatchersTests.java index 83590894083..d4eef94bac5 100644 --- a/spring-test/src/test/java/org/springframework/test/web/client/match/ContentRequestMatchersTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/client/match/ContentRequestMatchersTests.java @@ -128,23 +128,25 @@ public class ContentRequestMatchersTests { @Test public void testMultipartData() throws Exception { String contentType = "multipart/form-data;boundary=1234567890"; - String body = "--1234567890\r\n" + - "Content-Disposition: form-data; name=\"name 1\"\r\n" + - "\r\n" + - "vølue 1\r\n" + - "--1234567890\r\n" + - "Content-Disposition: form-data; name=\"name 2\"\r\n" + - "\r\n" + - "value 🙂\r\n" + - "--1234567890\r\n" + - "Content-Disposition: form-data; name=\"name 3\"\r\n" + - "\r\n" + - "value 漢字\r\n" + - "--1234567890\r\n" + - "Content-Disposition: form-data; name=\"name 4\"\r\n" + - "\r\n" + - "\r\n" + - "--1234567890--\r\n"; + String body = """ + --1234567890\r + Content-Disposition: form-data; name="name 1"\r + \r + vølue 1\r + --1234567890\r + Content-Disposition: form-data; name="name 2"\r + \r + value 🙂\r + --1234567890\r + Content-Disposition: form-data; name="name 3"\r + \r + value 漢字\r + --1234567890\r + Content-Disposition: form-data; name="name 4"\r + \r + \r + --1234567890--\r + """; this.request.getHeaders().setContentType(MediaType.parseMediaType(contentType)); this.request.getBody().write(body.getBytes(StandardCharsets.UTF_8)); @@ -160,23 +162,25 @@ public class ContentRequestMatchersTests { @Test public void testMultipartDataContains() throws Exception { String contentType = "multipart/form-data;boundary=1234567890"; - String body = "--1234567890\r\n" + - "Content-Disposition: form-data; name=\"name 1\"\r\n" + - "\r\n" + - "vølue 1\r\n" + - "--1234567890\r\n" + - "Content-Disposition: form-data; name=\"name 2\"\r\n" + - "\r\n" + - "value 🙂\r\n" + - "--1234567890\r\n" + - "Content-Disposition: form-data; name=\"name 3\"\r\n" + - "\r\n" + - "value 漢字\r\n" + - "--1234567890\r\n" + - "Content-Disposition: form-data; name=\"name 4\"\r\n" + - "\r\n" + - "\r\n" + - "--1234567890--\r\n"; + String body = """ + --1234567890\r + Content-Disposition: form-data; name="name 1"\r + \r + vølue 1\r + --1234567890\r + Content-Disposition: form-data; name="name 2"\r + \r + value 🙂\r + --1234567890\r + Content-Disposition: form-data; name="name 3"\r + \r + value 漢字\r + --1234567890\r + Content-Disposition: form-data; name="name 4"\r + \r + \r + --1234567890--\r + """; this.request.getHeaders().setContentType(MediaType.parseMediaType(contentType)); this.request.getBody().write(body.getBytes(StandardCharsets.UTF_8));