Revert "WIP"

This reverts commit c09b0f57631905a7b9cca32be5516853e4263ac0.
This commit is contained in:
Arjen Poutsma 2024-05-16 14:10:45 +02:00
parent 14861024d5
commit 67d2b2566e
5 changed files with 40 additions and 111 deletions

View File

@ -174,13 +174,12 @@ public class ContentRequestMatchers {
return formData(multiValueMap, false);
}
@SuppressWarnings("unchecked")
private RequestMatcher formData(MultiValueMap<String, String> expectedMap, boolean containsExactly) {
return request -> {
MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
MockHttpInputMessage message = new MockHttpInputMessage(mockRequest.getBodyAsBytes());
message.getHeaders().putAll(mockRequest.getHeaders());
MultiValueMap<String, String> actualMap = (MultiValueMap<String, String>) new FormHttpMessageConverter().read(null, message);
MultiValueMap<String, String> actualMap = new FormHttpMessageConverter().read(null, message);
if (containsExactly) {
assertEquals("Form data", expectedMap, actualMap);
}

View File

@ -899,7 +899,6 @@ public class MockHttpServletRequestBuilder
}
}
@SuppressWarnings("unchecked")
private MultiValueMap<String, String> parseFormData(MediaType mediaType) {
HttpInputMessage message = new HttpInputMessage() {
@Override
@ -915,7 +914,7 @@ public class MockHttpServletRequestBuilder
};
try {
return (MultiValueMap<String, String>) 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);

View File

@ -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<Map<String, ?>> {
public class FormHttpMessageConverter implements HttpMessageConverter<MultiValueMap<String, ?>> {
/** 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<Map<String
@Override
public boolean canRead(Class<?> 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<Map<String
}
@Override
public Map<String, ?> read(@Nullable Class<? extends Map<String, ?>> clazz,
public MultiValueMap<String, String> read(@Nullable Class<? extends MultiValueMap<String, ?>> clazz,
HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
MediaType contentType = inputMessage.getHeaders().getContentType();
@ -339,76 +339,41 @@ public class FormHttpMessageConverter implements HttpMessageConverter<Map<String
String body = StreamUtils.copyToString(inputMessage.getBody(), charset);
String[] pairs = StringUtils.tokenizeToStringArray(body, "&");
Map<String, String> svResult = null;
MultiValueMap<String, String> mvResult = null;
if (clazz == null || MultiValueMap.class.isAssignableFrom(clazz)) {
mvResult = new LinkedMultiValueMap<>(pairs.length);
}
else {
svResult = CollectionUtils.newLinkedHashMap(pairs.length);
}
MultiValueMap<String, String> 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<String, ?> map, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
public void write(MultiValueMap<String, ?> map, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
if (isMultipart(map, contentType)) {
writeMultipart((Map<String, Object>) map, contentType, outputMessage);
writeMultipart((MultiValueMap<String, Object>) map, contentType, outputMessage);
}
else {
writeForm((Map<String, Object>) map, contentType, outputMessage);
writeForm((MultiValueMap<String, Object>) map, contentType, outputMessage);
}
}
private boolean isMultipart(Map<String, ?> map, @Nullable MediaType contentType) {
private boolean isMultipart(MultiValueMap<String, ?> 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<Map<String
return false;
}
private void writeForm(Map<String, Object> formData, @Nullable MediaType mediaType,
private void writeForm(MultiValueMap<String, Object> formData, @Nullable MediaType mediaType,
HttpOutputMessage outputMessage) throws IOException {
mediaType = getFormContentType(mediaType);
@ -465,37 +430,30 @@ public class FormHttpMessageConverter implements HttpMessageConverter<Map<String
return contentType;
}
protected String serializeForm(Map<String, Object> formData, Charset charset) {
protected String serializeForm(MultiValueMap<String, Object> 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<String, Object> parts, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
MultiValueMap<String, Object> 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<Map<String
return (this.multipartCharset != null);
}
private void writeParts(OutputStream os, Map<String, Object> parts, byte[] boundary) throws IOException {
for (Map.Entry<String, Object> entry : parts.entrySet()) {
private void writeParts(OutputStream os, MultiValueMap<String, Object> parts, byte[] boundary) throws IOException {
for (Map.Entry<String, List<Object>> 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);
}
}

View File

@ -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<String, String> parseIfNecessary(HttpServletRequest request) throws IOException {
if (!shouldParse(request)) {
@ -107,7 +106,7 @@ public class FormContentFilter extends OncePerRequestFilter {
return request.getInputStream();
}
};
return (MultiValueMap<String, String>) this.formConverter.read(null, inputMessage);
return this.formConverter.read(null, inputMessage);
}
private boolean shouldParse(HttpServletRequest request) {

View File

@ -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<String, String> result = (MultiValueMap<String, String>) this.converter.read(null, inputMessage);
MultiValueMap<String, String> 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<String, String> result = (Map<String, String>) this.converter.read((Class<? extends Map<String, ?>>) 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<String, String> body = new LinkedMultiValueMap<>();