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 faf19a1314..a2eb29a010 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 @@ -174,13 +174,12 @@ public class ContentRequestMatchers { return formData(multiValueMap, false); } - @SuppressWarnings("unchecked") private RequestMatcher formData(MultiValueMap expectedMap, boolean containsExactly) { return request -> { MockClientHttpRequest mockRequest = (MockClientHttpRequest) request; MockHttpInputMessage message = new MockHttpInputMessage(mockRequest.getBodyAsBytes()); message.getHeaders().putAll(mockRequest.getHeaders()); - MultiValueMap actualMap = (MultiValueMap) new FormHttpMessageConverter().read(null, message); + MultiValueMap actualMap = new FormHttpMessageConverter().read(null, message); if (containsExactly) { assertEquals("Form data", expectedMap, actualMap); } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java index 6ec52e6671..433d78e86c 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java @@ -899,7 +899,6 @@ public class MockHttpServletRequestBuilder } } - @SuppressWarnings("unchecked") private MultiValueMap parseFormData(MediaType mediaType) { HttpInputMessage message = new HttpInputMessage() { @Override @@ -915,7 +914,7 @@ public class MockHttpServletRequestBuilder }; try { - return (MultiValueMap) new FormHttpMessageConverter().read(null, message); + return new FormHttpMessageConverter().read(null, message); } catch (IOException ex) { throw new IllegalStateException("Failed to parse form data in request body", ex); diff --git a/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java index 7da3310c90..7d325cbade 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java @@ -152,7 +152,7 @@ import org.springframework.util.StringUtils; * @see org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter * @see org.springframework.util.MultiValueMap */ -public class FormHttpMessageConverter implements HttpMessageConverter> { +public class FormHttpMessageConverter implements HttpMessageConverter> { /** The default charset used by the converter. */ public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; @@ -295,7 +295,7 @@ public class FormHttpMessageConverter implements HttpMessageConverter clazz, @Nullable MediaType mediaType) { - if (!Map.class.isAssignableFrom(clazz)) { + if (!MultiValueMap.class.isAssignableFrom(clazz)) { return false; } if (mediaType == null) { @@ -330,7 +330,7 @@ public class FormHttpMessageConverter implements HttpMessageConverter read(@Nullable Class> clazz, + public MultiValueMap read(@Nullable Class> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { MediaType contentType = inputMessage.getHeaders().getContentType(); @@ -339,76 +339,41 @@ public class FormHttpMessageConverter implements HttpMessageConverter svResult = null; - MultiValueMap mvResult = null; - if (clazz == null || MultiValueMap.class.isAssignableFrom(clazz)) { - mvResult = new LinkedMultiValueMap<>(pairs.length); - } - else { - svResult = CollectionUtils.newLinkedHashMap(pairs.length); - } + MultiValueMap result = new LinkedMultiValueMap<>(pairs.length); for (String pair : pairs) { int idx = pair.indexOf('='); if (idx == -1) { - String name = URLDecoder.decode(pair, charset); - if (mvResult != null) { - mvResult.add(name, null); - } - else if (svResult != null) { - svResult.put(name, null); - } + result.add(URLDecoder.decode(pair, charset), null); } else { String name = URLDecoder.decode(pair.substring(0, idx), charset); String value = URLDecoder.decode(pair.substring(idx + 1), charset); - if (mvResult != null) { - mvResult.add(name, value); - } - else if (svResult != null) { - svResult.put(name, value); - } + result.add(name, value); } } - if (mvResult != null) { - return mvResult; - } - else if (svResult != null) { - return svResult; - } - else { - throw new IllegalStateException(); - } + return result; } @Override @SuppressWarnings("unchecked") - public void write(Map map, @Nullable MediaType contentType, HttpOutputMessage outputMessage) + public void write(MultiValueMap map, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { if (isMultipart(map, contentType)) { - writeMultipart((Map) map, contentType, outputMessage); + writeMultipart((MultiValueMap) map, contentType, outputMessage); } else { - writeForm((Map) map, contentType, outputMessage); + writeForm((MultiValueMap) map, contentType, outputMessage); } } - private boolean isMultipart(Map map, @Nullable MediaType contentType) { + private boolean isMultipart(MultiValueMap map, @Nullable MediaType contentType) { if (contentType != null) { return contentType.getType().equalsIgnoreCase("multipart"); } - if (map instanceof MultiValueMap mvMap) { - for (List values : mvMap.values()) { - for (Object value : values) { - if (value != null && !(value instanceof String)) { - return true; - } - } - } - } - else { - for (Object value : map.values()) { + for (List values : map.values()) { + for (Object value : values) { if (value != null && !(value instanceof String)) { return true; } @@ -417,7 +382,7 @@ public class FormHttpMessageConverter implements HttpMessageConverter formData, @Nullable MediaType mediaType, + private void writeForm(MultiValueMap formData, @Nullable MediaType mediaType, HttpOutputMessage outputMessage) throws IOException { mediaType = getFormContentType(mediaType); @@ -465,37 +430,30 @@ public class FormHttpMessageConverter implements HttpMessageConverter formData, Charset charset) { + protected String serializeForm(MultiValueMap formData, Charset charset) { StringBuilder builder = new StringBuilder(); - formData.forEach((name, value) -> { + formData.forEach((name, values) -> { if (name == null) { - Assert.isTrue(value == null || value instanceof List values && values.isEmpty(), - () -> "Null name in form data: " + formData); + Assert.isTrue(CollectionUtils.isEmpty(values), () -> "Null name in form data: " + formData); return; } - if (value instanceof List values) { - values.forEach(v -> serializeFormValue(name, value, charset, builder)); - } - else { - serializeFormValue(name, value, charset, builder); - } + values.forEach(value -> { + if (builder.length() != 0) { + builder.append('&'); + } + builder.append(URLEncoder.encode(name, charset)); + if (value != null) { + builder.append('='); + builder.append(URLEncoder.encode(String.valueOf(value), charset)); + } + }); }); + return builder.toString(); } - private static void serializeFormValue(String name, @Nullable Object value, Charset charset, StringBuilder builder) { - if (!builder.isEmpty()) { - builder.append('&'); - } - builder.append(URLEncoder.encode(name, charset)); - if (value != null) { - builder.append('='); - builder.append(URLEncoder.encode(String.valueOf(value), charset)); - } - } - private void writeMultipart( - Map parts, @Nullable MediaType contentType, HttpOutputMessage outputMessage) + MultiValueMap parts, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException { // If the supplied content type is null, fall back to multipart/form-data. @@ -542,23 +500,13 @@ public class FormHttpMessageConverter implements HttpMessageConverter parts, byte[] boundary) throws IOException { - for (Map.Entry entry : parts.entrySet()) { + private void writeParts(OutputStream os, MultiValueMap parts, byte[] boundary) throws IOException { + for (Map.Entry> entry : parts.entrySet()) { String name = entry.getKey(); - Object value = entry.getValue(); - if (value instanceof List values) { - for (Object part : values) { - if (part != null) { - writeBoundary(os, boundary); - writePart(name, getHttpEntity(part), os); - writeNewLine(os); - } - } - } - else { - if (value != null) { + for (Object part : entry.getValue()) { + if (part != null) { writeBoundary(os, boundary); - writePart(name, getHttpEntity(value), os); + writePart(name, getHttpEntity(part), os); writeNewLine(os); } } diff --git a/spring-web/src/main/java/org/springframework/web/filter/FormContentFilter.java b/spring-web/src/main/java/org/springframework/web/filter/FormContentFilter.java index a0c29bf60f..bad214fb5d 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/FormContentFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/FormContentFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2021 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. @@ -94,7 +94,6 @@ public class FormContentFilter extends OncePerRequestFilter { } } - @SuppressWarnings("unchecked") @Nullable private MultiValueMap parseIfNecessary(HttpServletRequest request) throws IOException { if (!shouldParse(request)) { @@ -107,7 +106,7 @@ public class FormContentFilter extends OncePerRequestFilter { return request.getInputStream(); } }; - return (MultiValueMap) this.formConverter.read(null, inputMessage); + return this.formConverter.read(null, inputMessage); } private boolean shouldParse(HttpServletRequest request) { diff --git a/spring-web/src/test/java/org/springframework/http/converter/FormHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/FormHttpMessageConverterTests.java index 3312e3b7e8..98c937bc73 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/FormHttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/FormHttpMessageConverterTests.java @@ -117,14 +117,13 @@ class FormHttpMessageConverterTests { assertCanWrite(MULTIPART_RELATED); } - @SuppressWarnings("unchecked") @Test - void readMultiValueForm() throws Exception { + void readForm() throws Exception { String body = "name+1=value+1&name+2=value+2%2B1&name+2=value+2%2B2&name+3"; MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes(StandardCharsets.ISO_8859_1)); inputMessage.getHeaders().setContentType( new MediaType("application", "x-www-form-urlencoded", StandardCharsets.ISO_8859_1)); - MultiValueMap result = (MultiValueMap) this.converter.read(null, inputMessage); + MultiValueMap result = this.converter.read(null, inputMessage); assertThat(result).as("Invalid result").hasSize(3); assertThat(result.getFirst("name 1")).as("Invalid result").isEqualTo("value 1"); @@ -133,21 +132,6 @@ class FormHttpMessageConverterTests { assertThat(result.getFirst("name 3")).as("Invalid result").isNull(); } - @SuppressWarnings("unchecked") - @Test - void readSingleValueForm() throws Exception { - String body = "name+1=value+1&name+2=value+2&name+3"; - MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes(StandardCharsets.ISO_8859_1)); - inputMessage.getHeaders().setContentType( - new MediaType("application", "x-www-form-urlencoded", StandardCharsets.ISO_8859_1)); - Map result = (Map) this.converter.read((Class>) Map.class, inputMessage); - - assertThat(result).as("Invalid result").hasSize(3); - assertThat(result.get("name 1")).as("Invalid result").isEqualTo("value 1"); - assertThat(result.get("name 2")).as("Invalid result").isEqualTo("value 2"); - assertThat(result.get("name 3")).as("Invalid result").isNull(); - } - @Test void writeForm() throws IOException { MultiValueMap body = new LinkedMultiValueMap<>();