diff --git a/framework-docs/src/docs/asciidoc/web/webflux.adoc b/framework-docs/src/docs/asciidoc/web/webflux.adoc index a0854315c4..514f2cb7e7 100644 --- a/framework-docs/src/docs/asciidoc/web/webflux.adoc +++ b/framework-docs/src/docs/asciidoc/web/webflux.adoc @@ -613,8 +613,8 @@ The `DefaultServerWebExchange` uses the configured `HttpMessageReader` to parse ---- The `DefaultServerWebExchange` uses the configured -`HttpMessageReader>` to parse `multipart/form-data` content -into a `MultiValueMap`. +`HttpMessageReader>` to parse `multipart/form-data`, +`multipart/mixed` and `multipart/related` content into a `MultiValueMap`. By default, this is the `DefaultPartHttpMessageReader`, which does not have any third-party dependencies. 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 ==== Multipart `MultipartHttpMessageReader` and `MultipartHttpMessageWriter` support decoding and -encoding "multipart/form-data" content. In turn `MultipartHttpMessageReader` delegates to -another `HttpMessageReader` for the actual parsing to a `Flux` and then simply -collects the parts into a `MultiValueMap`. +encoding "multipart/form-data", "multipart/mixed" and "multipart/related" content. +In turn `MultipartHttpMessageReader` delegates to another `HttpMessageReader` +for the actual parsing to a `Flux` and then simply collects the parts into a `MultiValueMap`. By default, the `DefaultPartHttpMessageReader` is used, but this can be changed through the `ServerCodecConfigurer`. For more information about the `DefaultPartHttpMessageReader`, refer to the diff --git a/spring-web/src/main/java/org/springframework/web/server/adapter/DefaultServerWebExchange.java b/spring-web/src/main/java/org/springframework/web/server/adapter/DefaultServerWebExchange.java index dc3537a0f8..83b7a7ed30 100644 --- a/spring-web/src/main/java/org/springframework/web/server/adapter/DefaultServerWebExchange.java +++ b/spring-web/src/main/java/org/springframework/web/server/adapter/DefaultServerWebExchange.java @@ -163,9 +163,11 @@ public class DefaultServerWebExchange implements ServerWebExchange { try { MediaType contentType = request.getHeaders().getContentType(); - if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) { + if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType) || + MediaType.MULTIPART_MIXED.isCompatibleWith(contentType) || + MediaType.MULTIPART_RELATED.isCompatibleWith(contentType)) { return ((HttpMessageReader>) configurer.getReaders().stream() - .filter(reader -> reader.canRead(MULTIPART_DATA_TYPE, MediaType.MULTIPART_FORM_DATA)) + .filter(reader -> reader.canRead(MULTIPART_DATA_TYPE, contentType)) .findFirst() .orElseThrow(() -> new IllegalStateException("No multipart HttpMessageReader."))) .readMono(MULTIPART_DATA_TYPE, request, Hints.from(Hints.LOG_PREFIX_HINT, logPrefix)) diff --git a/spring-web/src/test/java/org/springframework/http/server/reactive/MultipartHttpHandlerIntegrationTests.java b/spring-web/src/test/java/org/springframework/http/server/reactive/MultipartHttpHandlerIntegrationTests.java index 59c8a87da7..62fb1c7db2 100644 --- a/spring-web/src/test/java/org/springframework/http/server/reactive/MultipartHttpHandlerIntegrationTests.java +++ b/spring-web/src/test/java/org/springframework/http/server/reactive/MultipartHttpHandlerIntegrationTests.java @@ -32,6 +32,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.http.codec.multipart.FilePart; import org.springframework.http.codec.multipart.FormFieldPart; import org.springframework.http.codec.multipart.Part; +import org.springframework.http.converter.FormHttpMessageConverter; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; @@ -55,14 +56,38 @@ class MultipartHttpHandlerIntegrationTests extends AbstractHttpHandlerIntegratio } @ParameterizedHttpServerTest - void getFormParts(HttpServer httpServer) throws Exception { + void getFormPartsFormdata(HttpServer httpServer) throws Exception { + performTest(httpServer, MediaType.MULTIPART_FORM_DATA); + } + + @ParameterizedHttpServerTest + void getFormPartsMixed(HttpServer httpServer) throws Exception { + performTest(httpServer, MediaType.MULTIPART_MIXED); + } + + @ParameterizedHttpServerTest + void getFormPartsRelated(HttpServer httpServer) throws Exception { + RestTemplate restTemplate = new RestTemplate(); + restTemplate.getMessageConverters().stream() + .filter(FormHttpMessageConverter.class::isInstance) + .map(FormHttpMessageConverter.class::cast) + .findFirst() + .orElseThrow() + .addSupportedMediaTypes(MediaType.MULTIPART_RELATED); + performTest(httpServer, MediaType.MULTIPART_RELATED, restTemplate); + } + + private void performTest(HttpServer httpServer, MediaType mediaType) throws Exception { + performTest(httpServer, mediaType, new RestTemplate()); + } + + private void performTest(HttpServer httpServer, MediaType mediaType, RestTemplate restTemplate) throws Exception { startServer(httpServer); @SuppressWarnings("resource") - RestTemplate restTemplate = new RestTemplate(); RequestEntity> request = RequestEntity .post(URI.create("http://localhost:" + port + "/form-parts")) - .contentType(MediaType.MULTIPART_FORM_DATA) + .contentType(mediaType) .body(generateBody()); ResponseEntity response = restTemplate.exchange(request, Void.class); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);