diff --git a/spring-web/src/main/java/org/springframework/http/codec/ClientCodecConfigurer.java b/spring-web/src/main/java/org/springframework/http/codec/ClientCodecConfigurer.java
index 2ff85f15a5a..62b85df1b09 100644
--- a/spring-web/src/main/java/org/springframework/http/codec/ClientCodecConfigurer.java
+++ b/spring-web/src/main/java/org/springframework/http/codec/ClientCodecConfigurer.java
@@ -17,6 +17,7 @@
package org.springframework.http.codec;
import org.springframework.core.codec.Decoder;
+import org.springframework.core.codec.Encoder;
/**
* Helps to configure a list of client-side HTTP message readers and writers
@@ -55,6 +56,13 @@ public interface ClientCodecConfigurer extends CodecConfigurer {
*/
interface ClientDefaultCodecsConfigurer extends DefaultCodecsConfigurer {
+ /**
+ * Configure encoders or writers for use with
+ * {@link org.springframework.http.codec.multipart.MultipartHttpMessageWriter
+ * MultipartHttpMessageWriter}.
+ */
+ MultipartCodecsConfigurer multipartCodecs();
+
/**
* Configure the {@code Decoder} to use for Server-Sent Events.
*
By default the {@link #jackson2Decoder} override is used for SSE.
@@ -63,5 +71,25 @@ public interface ClientCodecConfigurer extends CodecConfigurer {
void serverSentEventDecoder(Decoder> decoder);
}
+ /**
+ * Registry and container for multipart HTTP message writers.
+ */
+ interface MultipartCodecsConfigurer {
+
+ /**
+ * Add a Part {@code Encoder}, internally wrapped with
+ * {@link EncoderHttpMessageWriter}.
+ * @param encoder the encoder to add
+ */
+ MultipartCodecsConfigurer encoder(Encoder> encoder);
+
+ /**
+ * Add a Part {@link HttpMessageWriter}. For writers of type
+ * {@link EncoderHttpMessageWriter} consider using the shortcut
+ * {@link #encoder(Encoder)} instead.
+ * @param writer the writer to add
+ */
+ MultipartCodecsConfigurer writer(HttpMessageWriter> writer);
+ }
}
diff --git a/spring-web/src/main/java/org/springframework/http/codec/CodecConfigurer.java b/spring-web/src/main/java/org/springframework/http/codec/CodecConfigurer.java
index daf5d9be509..25971dbf260 100644
--- a/spring-web/src/main/java/org/springframework/http/codec/CodecConfigurer.java
+++ b/spring-web/src/main/java/org/springframework/http/codec/CodecConfigurer.java
@@ -108,7 +108,7 @@ public interface CodecConfigurer {
void reader(HttpMessageReader> reader);
/**
- * Add a custom {@link HttpMessageWriter}. For readers of type
+ * Add a custom {@link HttpMessageWriter}. For writers of type
* {@link EncoderHttpMessageWriter} consider using the shortcut
* {@link #encoder(Encoder)} instead.
* @param writer the writer to add
diff --git a/spring-web/src/main/java/org/springframework/http/codec/DefaultClientCodecConfigurer.java b/spring-web/src/main/java/org/springframework/http/codec/DefaultClientCodecConfigurer.java
index a6c055154ee..8d4463602a0 100644
--- a/spring-web/src/main/java/org/springframework/http/codec/DefaultClientCodecConfigurer.java
+++ b/spring-web/src/main/java/org/springframework/http/codec/DefaultClientCodecConfigurer.java
@@ -16,9 +16,11 @@
package org.springframework.http.codec;
+import java.util.ArrayList;
import java.util.List;
import org.springframework.core.codec.Decoder;
+import org.springframework.core.codec.Encoder;
import org.springframework.core.codec.StringDecoder;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.multipart.MultipartHttpMessageWriter;
@@ -48,6 +50,17 @@ class DefaultClientCodecConfigurer extends DefaultCodecConfigurer implements Cli
extends AbstractDefaultCodecsConfigurer
implements ClientCodecConfigurer.ClientDefaultCodecsConfigurer {
+ private DefaultMultipartCodecsConfigurer multipartCodecs;
+
+
+ @Override
+ public MultipartCodecsConfigurer multipartCodecs() {
+ if (this.multipartCodecs == null) {
+ this.multipartCodecs = new DefaultMultipartCodecsConfigurer();
+ }
+ return this.multipartCodecs;
+ }
+
@Override
public void serverSentEventDecoder(Decoder> decoder) {
HttpMessageReader> reader = new ServerSentEventHttpMessageReader(decoder);
@@ -58,7 +71,10 @@ class DefaultClientCodecConfigurer extends DefaultCodecConfigurer implements Cli
protected void addTypedWritersTo(List> result) {
super.addTypedWritersTo(result);
addWriterTo(result, FormHttpMessageWriter::new);
- addWriterTo(result, MultipartHttpMessageWriter::new);
+ addWriterTo(result, () -> findWriter(MultipartHttpMessageWriter.class,
+ () -> this.multipartCodecs != null ?
+ new MultipartHttpMessageWriter(this.multipartCodecs.getWriters()) :
+ new MultipartHttpMessageWriter()));
}
@Override
@@ -89,7 +105,28 @@ class DefaultClientCodecConfigurer extends DefaultCodecConfigurer implements Cli
addReaderTo(result,
() -> new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes(false)));
}
+ }
+
+ private static class DefaultMultipartCodecsConfigurer implements MultipartCodecsConfigurer {
+
+ private final List> writers = new ArrayList<>();
+
+
+ @Override
+ public MultipartCodecsConfigurer encoder(Encoder> encoder) {
+ writer(new EncoderHttpMessageWriter<>(encoder));
+ return this;
+ }
+ @Override
+ public MultipartCodecsConfigurer writer(HttpMessageWriter> writer) {
+ this.writers.add(writer);
+ return this;
+ }
+
+ public List> getWriters() {
+ return this.writers;
+ }
}
}
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ControllerMethodResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ControllerMethodResolver.java
index 1dd1799e76e..08d697a8026 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ControllerMethodResolver.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ControllerMethodResolver.java
@@ -134,10 +134,10 @@ class ControllerMethodResolver {
// Annotation-based...
registrar.add(new RequestParamMethodArgumentResolver(beanFactory, reactiveRegistry, false));
registrar.add(new RequestParamMapMethodArgumentResolver(reactiveRegistry));
- registrar.add(new RequestPartMethodArgumentResolver(reactiveRegistry));
registrar.add(new PathVariableMethodArgumentResolver(beanFactory, reactiveRegistry));
registrar.add(new PathVariableMapMethodArgumentResolver(reactiveRegistry));
registrar.addIfRequestBody(readers -> new RequestBodyArgumentResolver(readers, reactiveRegistry));
+ registrar.addIfRequestBody(readers -> new RequestPartMethodArgumentResolver(readers, reactiveRegistry));
registrar.addIfModelAttribute(() -> new ModelAttributeMethodArgumentResolver(reactiveRegistry, false));
registrar.add(new RequestHeaderMethodArgumentResolver(beanFactory, reactiveRegistry));
registrar.add(new RequestHeaderMapMethodArgumentResolver(reactiveRegistry));
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestPartMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestPartMethodArgumentResolver.java
index 0cd3d7f13a7..0eb1e1a12f8 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestPartMethodArgumentResolver.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestPartMethodArgumentResolver.java
@@ -16,80 +16,131 @@
package org.springframework.web.reactive.result.method.annotation;
+import java.util.Collections;
import java.util.List;
+import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.MethodParameter;
import org.springframework.core.ReactiveAdapter;
import org.springframework.core.ReactiveAdapterRegistry;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.multipart.Part;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestPart;
-import org.springframework.web.bind.annotation.ValueConstants;
+import org.springframework.web.reactive.BindingContext;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebInputException;
/**
- * Resolver for method arguments annotated with @{@link RequestPart}.
+ * Resolver for {@code @RequestPart} arguments where the named part is decoded
+ * much like an {@code @RequestBody} argument but based on the content of an
+ * individual part instead. The arguments may be wrapped with a reactive type
+ * for a single value (e.g. Reactor {@code Mono}, RxJava {@code Single}).
+ *
+ * This resolver also supports arguments of type {@link Part} which may be
+ * wrapped with are reactive type for a single or multiple values.
*
- * @author Sebastien Deleuze
* @author Rossen Stoyanchev
* @since 5.0
*/
-public class RequestPartMethodArgumentResolver extends AbstractNamedValueArgumentResolver {
-
- /**
- * Class constructor with a default resolution mode flag.
- * @param registry for checking reactive type wrappers
- */
- public RequestPartMethodArgumentResolver(ReactiveAdapterRegistry registry) {
- super(null, registry);
+public class RequestPartMethodArgumentResolver extends AbstractMessageReaderArgumentResolver {
+
+
+ public RequestPartMethodArgumentResolver(List> readers,
+ ReactiveAdapterRegistry registry) {
+
+ super(readers, registry);
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
- return parameter.hasParameterAnnotation(RequestPart.class);
+ return parameter.hasParameterAnnotation(RequestPart.class) ||
+ checkParameterType(parameter, Part.class::isAssignableFrom);
}
@Override
- protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
- RequestPart ann = parameter.getParameterAnnotation(RequestPart.class);
- return (ann != null ? new RequestPartNamedValueInfo(ann) : new RequestPartNamedValueInfo());
+ public Mono