Skip to content

Commit bcfbde9

Browse files
committed
Parse parts in MockMultipartHttpServletRequestBuilder
Closes gh-26261
1 parent 17e6cf1 commit bcfbde9

File tree

3 files changed

+67
-33
lines changed

3 files changed

+67
-33
lines changed

spring-test/src/main/java/org/springframework/test/web/servlet/TestDispatcherServlet.java

-5
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import org.springframework.web.context.request.async.DeferredResult;
3636
import org.springframework.web.context.request.async.DeferredResultProcessingInterceptor;
3737
import org.springframework.web.context.request.async.WebAsyncUtils;
38-
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;
3938
import org.springframework.web.servlet.DispatcherServlet;
4039
import org.springframework.web.servlet.HandlerExecutionChain;
4140
import org.springframework.web.servlet.ModelAndView;
@@ -68,10 +67,6 @@ public TestDispatcherServlet(WebApplicationContext webApplicationContext) {
6867
protected void service(HttpServletRequest request, HttpServletResponse response)
6968
throws ServletException, IOException {
7069

71-
if (!request.getParts().isEmpty()) {
72-
request = new StandardMultipartHttpServletRequest(request);
73-
}
74-
7570
registerAsyncResultInterceptors(request);
7671

7772
super.service(request, response);

spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilder.java

+40-16
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,28 @@
1717
package org.springframework.test.web.servlet.request;
1818

1919
import java.io.IOException;
20+
import java.io.InputStreamReader;
2021
import java.net.URI;
22+
import java.nio.charset.Charset;
23+
import java.nio.charset.StandardCharsets;
2124
import java.util.ArrayList;
2225
import java.util.Collection;
2326
import java.util.List;
2427

2528
import javax.servlet.ServletContext;
2629
import javax.servlet.http.Part;
2730

28-
import org.springframework.http.HttpHeaders;
2931
import org.springframework.http.HttpMethod;
3032
import org.springframework.http.MediaType;
3133
import org.springframework.lang.Nullable;
3234
import org.springframework.mock.web.MockHttpServletRequest;
3335
import org.springframework.mock.web.MockMultipartFile;
3436
import org.springframework.mock.web.MockMultipartHttpServletRequest;
35-
import org.springframework.mock.web.MockPart;
3637
import org.springframework.util.Assert;
38+
import org.springframework.util.FileCopyUtils;
3739
import org.springframework.util.LinkedMultiValueMap;
3840
import org.springframework.util.MultiValueMap;
41+
import org.springframework.web.multipart.MultipartFile;
3942

4043
/**
4144
* Default builder for {@link MockMultipartHttpServletRequest}.
@@ -141,26 +144,47 @@ public Object merge(@Nullable Object parent) {
141144
@Override
142145
protected final MockHttpServletRequest createServletRequest(ServletContext servletContext) {
143146
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest(servletContext);
144-
this.files.forEach(file -> request.addPart(toMockPart(file)));
145-
this.parts.values().stream().flatMap(Collection::stream).forEach(request::addPart);
146-
return request;
147-
}
148-
149-
private MockPart toMockPart(MockMultipartFile file) {
150-
byte[] bytes = null;
151-
if (!file.isEmpty()) {
147+
this.files.forEach(request::addFile);
148+
this.parts.values().stream().flatMap(Collection::stream).forEach(part -> {
149+
request.addPart(part);
152150
try {
153-
bytes = file.getBytes();
151+
MultipartFile file = asMultipartFile(part);
152+
if (file != null) {
153+
request.addFile(file);
154+
return;
155+
}
156+
String value = toParameterValue(part);
157+
if (value != null) {
158+
request.addParameter(part.getName(), toParameterValue(part));
159+
}
154160
}
155161
catch (IOException ex) {
156-
throw new IllegalStateException("Unexpected IOException", ex);
162+
throw new IllegalStateException("Failed to read content for part " + part.getName(), ex);
157163
}
164+
});
165+
return request;
166+
}
167+
168+
@Nullable
169+
private MultipartFile asMultipartFile(Part part) throws IOException {
170+
String name = part.getName();
171+
String filename = part.getSubmittedFileName();
172+
if (filename != null) {
173+
return new MockMultipartFile(name, filename, part.getContentType(), part.getInputStream());
158174
}
159-
MockPart part = new MockPart(file.getName(), file.getOriginalFilename(), bytes);
160-
if (file.getContentType() != null) {
161-
part.getHeaders().set(HttpHeaders.CONTENT_TYPE, file.getContentType());
175+
return null;
176+
}
177+
178+
@Nullable
179+
private String toParameterValue(Part part) throws IOException {
180+
String rawType = part.getContentType();
181+
MediaType mediaType = (rawType != null ? MediaType.parseMediaType(rawType) : MediaType.TEXT_PLAIN);
182+
if (!mediaType.isCompatibleWith(MediaType.TEXT_PLAIN)) {
183+
return null;
162184
}
163-
return part;
185+
Charset charset = (mediaType.getCharset() != null ? mediaType.getCharset() : StandardCharsets.UTF_8);
186+
InputStreamReader reader = new InputStreamReader(part.getInputStream(), charset);
187+
return FileCopyUtils.copyToString(reader);
164188
}
165189

166190
}

spring-test/src/test/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilderTests.java

+27-12
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,19 @@
1616

1717
package org.springframework.test.web.servlet.request;
1818

19-
import java.nio.charset.StandardCharsets;
20-
2119
import javax.servlet.http.Part;
2220

2321
import org.junit.jupiter.api.Test;
2422

2523
import org.springframework.http.HttpMethod;
24+
import org.springframework.http.MediaType;
2625
import org.springframework.mock.web.MockHttpServletRequest;
2726
import org.springframework.mock.web.MockMultipartFile;
27+
import org.springframework.mock.web.MockMultipartHttpServletRequest;
2828
import org.springframework.mock.web.MockPart;
2929
import org.springframework.mock.web.MockServletContext;
30-
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;
3130

31+
import static java.nio.charset.StandardCharsets.UTF_8;
3232
import static org.assertj.core.api.Assertions.assertThat;
3333

3434
/**
@@ -38,17 +38,32 @@
3838
public class MockMultipartHttpServletRequestBuilderTests {
3939

4040
@Test // gh-26166
41-
void addFilesAndParts() throws Exception {
42-
MockHttpServletRequest mockRequest = new MockMultipartHttpServletRequestBuilder("/upload")
43-
.file(new MockMultipartFile("file", "test.txt", "text/plain", "Test".getBytes(StandardCharsets.UTF_8)))
44-
.part(new MockPart("data", "{\"node\":\"node\"}".getBytes(StandardCharsets.UTF_8)))
45-
.buildRequest(new MockServletContext());
41+
void addFileAndParts() throws Exception {
42+
MockMultipartHttpServletRequest mockRequest =
43+
(MockMultipartHttpServletRequest) new MockMultipartHttpServletRequestBuilder("/upload")
44+
.file(new MockMultipartFile("file", "test.txt", "text/plain", "Test".getBytes(UTF_8)))
45+
.part(new MockPart("name", "value".getBytes(UTF_8)))
46+
.buildRequest(new MockServletContext());
47+
48+
assertThat(mockRequest.getFileMap()).containsOnlyKeys("file");
49+
assertThat(mockRequest.getParameterMap()).containsOnlyKeys("name");
50+
assertThat(mockRequest.getParts()).extracting(Part::getName).containsExactly("name");
51+
}
52+
53+
@Test // gh-26261
54+
void addFileWithoutFilename() throws Exception {
55+
MockPart jsonPart = new MockPart("data", "{\"node\":\"node\"}".getBytes(UTF_8));
56+
jsonPart.getHeaders().setContentType(MediaType.APPLICATION_JSON);
4657

47-
StandardMultipartHttpServletRequest parsedRequest = new StandardMultipartHttpServletRequest(mockRequest);
58+
MockMultipartHttpServletRequest mockRequest =
59+
(MockMultipartHttpServletRequest) new MockMultipartHttpServletRequestBuilder("/upload")
60+
.file(new MockMultipartFile("file", "Test".getBytes(UTF_8)))
61+
.part(jsonPart)
62+
.buildRequest(new MockServletContext());
4863

49-
assertThat(parsedRequest.getParameterMap()).containsOnlyKeys("data");
50-
assertThat(parsedRequest.getFileMap()).containsOnlyKeys("file");
51-
assertThat(parsedRequest.getParts()).extracting(Part::getName).containsExactly("file", "data");
64+
assertThat(mockRequest.getFileMap()).containsOnlyKeys("file");
65+
assertThat(mockRequest.getParameterMap()).isEmpty();
66+
assertThat(mockRequest.getParts()).extracting(Part::getName).containsExactly("data");
5267
}
5368

5469
@Test

0 commit comments

Comments
 (0)