Skip to content

Commit 67df075

Browse files
sephiroth-jrstoyanchev
authored andcommitted
Support multipart/mixed and related in DefaultServerWebExchange
See gh-29671
1 parent 17bc3fa commit 67df075

File tree

3 files changed

+37
-10
lines changed

3 files changed

+37
-10
lines changed

framework-docs/src/docs/asciidoc/web/webflux.adoc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -613,8 +613,8 @@ The `DefaultServerWebExchange` uses the configured `HttpMessageReader` to parse
613613
----
614614

615615
The `DefaultServerWebExchange` uses the configured
616-
`HttpMessageReader<MultiValueMap<String, Part>>` to parse `multipart/form-data` content
617-
into a `MultiValueMap`.
616+
`HttpMessageReader<MultiValueMap<String, Part>>` to parse `multipart/form-data`,
617+
`multipart/mixed` and `multipart/related` content into a `MultiValueMap`.
618618
By default, this is the `DefaultPartHttpMessageReader`, which does not have any third-party
619619
dependencies.
620620
Alternatively, the `SynchronossPartHttpMessageReader` can be used, which is based on the
@@ -805,9 +805,9 @@ consistently for access to the cached form data versus reading from the raw requ
805805
==== Multipart
806806

807807
`MultipartHttpMessageReader` and `MultipartHttpMessageWriter` support decoding and
808-
encoding "multipart/form-data" content. In turn `MultipartHttpMessageReader` delegates to
809-
another `HttpMessageReader` for the actual parsing to a `Flux<Part>` and then simply
810-
collects the parts into a `MultiValueMap`.
808+
encoding "multipart/form-data", "multipart/mixed" and "multipart/related" content.
809+
In turn `MultipartHttpMessageReader` delegates to another `HttpMessageReader`
810+
for the actual parsing to a `Flux<Part>` and then simply collects the parts into a `MultiValueMap`.
811811
By default, the `DefaultPartHttpMessageReader` is used, but this can be changed through the
812812
`ServerCodecConfigurer`.
813813
For more information about the `DefaultPartHttpMessageReader`, refer to the

spring-web/src/main/java/org/springframework/web/server/adapter/DefaultServerWebExchange.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,11 @@ private static Mono<MultiValueMap<String, Part>> initMultipartData(ServerHttpReq
163163

164164
try {
165165
MediaType contentType = request.getHeaders().getContentType();
166-
if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) {
166+
if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType) ||
167+
MediaType.MULTIPART_MIXED.isCompatibleWith(contentType) ||
168+
MediaType.MULTIPART_RELATED.isCompatibleWith(contentType)) {
167169
return ((HttpMessageReader<MultiValueMap<String, Part>>) configurer.getReaders().stream()
168-
.filter(reader -> reader.canRead(MULTIPART_DATA_TYPE, MediaType.MULTIPART_FORM_DATA))
170+
.filter(reader -> reader.canRead(MULTIPART_DATA_TYPE, contentType))
169171
.findFirst()
170172
.orElseThrow(() -> new IllegalStateException("No multipart HttpMessageReader.")))
171173
.readMono(MULTIPART_DATA_TYPE, request, Hints.from(Hints.LOG_PREFIX_HINT, logPrefix))

spring-web/src/test/java/org/springframework/http/server/reactive/MultipartHttpHandlerIntegrationTests.java

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.springframework.http.codec.multipart.FilePart;
3333
import org.springframework.http.codec.multipart.FormFieldPart;
3434
import org.springframework.http.codec.multipart.Part;
35+
import org.springframework.http.converter.FormHttpMessageConverter;
3536
import org.springframework.util.LinkedMultiValueMap;
3637
import org.springframework.util.MultiValueMap;
3738
import org.springframework.web.client.RestTemplate;
@@ -55,14 +56,38 @@ protected HttpHandler createHttpHandler() {
5556
}
5657

5758
@ParameterizedHttpServerTest
58-
void getFormParts(HttpServer httpServer) throws Exception {
59+
void getFormPartsFormdata(HttpServer httpServer) throws Exception {
60+
performTest(httpServer, MediaType.MULTIPART_FORM_DATA);
61+
}
62+
63+
@ParameterizedHttpServerTest
64+
void getFormPartsMixed(HttpServer httpServer) throws Exception {
65+
performTest(httpServer, MediaType.MULTIPART_MIXED);
66+
}
67+
68+
@ParameterizedHttpServerTest
69+
void getFormPartsRelated(HttpServer httpServer) throws Exception {
70+
RestTemplate restTemplate = new RestTemplate();
71+
restTemplate.getMessageConverters().stream()
72+
.filter(FormHttpMessageConverter.class::isInstance)
73+
.map(FormHttpMessageConverter.class::cast)
74+
.findFirst()
75+
.orElseThrow()
76+
.addSupportedMediaTypes(MediaType.MULTIPART_RELATED);
77+
performTest(httpServer, MediaType.MULTIPART_RELATED, restTemplate);
78+
}
79+
80+
private void performTest(HttpServer httpServer, MediaType mediaType) throws Exception {
81+
performTest(httpServer, mediaType, new RestTemplate());
82+
}
83+
84+
private void performTest(HttpServer httpServer, MediaType mediaType, RestTemplate restTemplate) throws Exception {
5985
startServer(httpServer);
6086

6187
@SuppressWarnings("resource")
62-
RestTemplate restTemplate = new RestTemplate();
6388
RequestEntity<MultiValueMap<String, Object>> request = RequestEntity
6489
.post(URI.create("http://localhost:" + port + "/form-parts"))
65-
.contentType(MediaType.MULTIPART_FORM_DATA)
90+
.contentType(mediaType)
6691
.body(generateBody());
6792
ResponseEntity<Void> response = restTemplate.exchange(request, Void.class);
6893
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);

0 commit comments

Comments
 (0)