diff --git a/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java index 5e7b23b9da..bbc08b3c68 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java +++ b/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java @@ -134,6 +134,14 @@ public class MultipartHttpMessageWriter implements HttpMessageWriter> getPartWriters() { + return Collections.unmodifiableList(partWriters); + } + /** * Set the character set to use for part headers such as * "Content-Disposition" (and its filename parameter). diff --git a/spring-web/src/main/java/org/springframework/http/codec/support/BaseCodecConfigurer.java b/spring-web/src/main/java/org/springframework/http/codec/support/BaseCodecConfigurer.java index 2bf2ffc914..8d435ad9d9 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/support/BaseCodecConfigurer.java +++ b/spring-web/src/main/java/org/springframework/http/codec/support/BaseCodecConfigurer.java @@ -18,7 +18,6 @@ package org.springframework.http.codec.support; import java.util.ArrayList; import java.util.List; -import java.util.function.Supplier; import org.springframework.core.ResolvableType; import org.springframework.core.codec.Decoder; @@ -28,6 +27,7 @@ import org.springframework.http.codec.DecoderHttpMessageReader; import org.springframework.http.codec.EncoderHttpMessageWriter; import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.codec.HttpMessageWriter; +import org.springframework.http.codec.multipart.MultipartHttpMessageWriter; import org.springframework.util.Assert; /** @@ -85,12 +85,22 @@ class BaseCodecConfigurer implements CodecConfigurer { @Override public List> getWriters() { + return getWritersInternal(false); + } + + /** + * Internal method that returns the configured writers. + * @param forMultipart whether to returns writers for general use ("false"), + * or for multipart requests only ("true"). Generally the two sets are the + * same except for the multipart writer itself. + */ + protected List> getWritersInternal(boolean forMultipart) { List> result = new ArrayList<>(); - result.addAll(this.defaultCodecs.getTypedWriters()); + result.addAll(this.defaultCodecs.getTypedWriters(forMultipart)); result.addAll(this.customCodecs.getTypedWriters()); - result.addAll(this.defaultCodecs.getObjectWriters()); + result.addAll(this.defaultCodecs.getObjectWriters(forMultipart)); result.addAll(this.customCodecs.getObjectWriters()); result.addAll(this.defaultCodecs.getCatchAllWriters()); @@ -98,17 +108,6 @@ class BaseCodecConfigurer implements CodecConfigurer { } - // Accessors for use in sub-classes... - - protected Supplier>> getCustomTypedWriters() { - return () -> this.customCodecs.typedWriters; - } - - protected Supplier>> getCustomObjectWriters() { - return () -> this.customCodecs.objectWriters; - } - - /** * Default implementation of {@code CustomCodecs}. */ diff --git a/spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java b/spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java index dc1183bd8f..cdeb47548c 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java +++ b/spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java @@ -95,8 +95,9 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs { } - // Package private access to the configured readers... - + /** + * Return readers that support specific types. + */ final List> getTypedReaders() { if (!this.registerDefaults) { return Collections.emptyList(); @@ -112,9 +113,15 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs { return readers; } + /** + * Hook for client or server specific typed readers. + */ protected void extendTypedReaders(List> typedReaders) { } + /** + * Return Object readers (JSON, XML, SSE). + */ final List> getObjectReaders() { if (!this.registerDefaults) { return Collections.emptyList(); @@ -133,9 +140,15 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs { return readers; } + /** + * Hook for client or server specific Object readers. + */ protected void extendObjectReaders(List> objectReaders) { } + /** + * Return readers that need to be at the end, after all others. + */ final List> getCatchAllReaders() { if (!this.registerDefaults) { return Collections.emptyList(); @@ -145,10 +158,13 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs { return result; } - - // Package private access to the configured writers... - - final List> getTypedWriters() { + /** + * Return writers that support specific types. + * @param forMultipart whether to returns writers for general use ("false"), + * or for multipart requests only ("true"). Generally the two sets are the + * same except for the multipart writer itself. + */ + final List> getTypedWriters(boolean forMultipart) { if (!this.registerDefaults) { return Collections.emptyList(); } @@ -158,14 +174,26 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs { writers.add(new EncoderHttpMessageWriter<>(new DataBufferEncoder())); writers.add(new ResourceHttpMessageWriter()); writers.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.textPlainOnly())); - extendTypedWriters(writers); + // No client or server specific multipart writers currently.. + if (!forMultipart) { + extendTypedWriters(writers); + } return writers; } + /** + * Hook for client or server specific typed writers. + */ protected void extendTypedWriters(List> typedWriters) { } - final List> getObjectWriters() { + /** + * Return Object writers (JSON, XML, SSE). + * @param forMultipart whether to returns writers for general use ("false"), + * or for multipart requests only ("true"). Generally the two sets are the + * same except for the multipart writer itself. + */ + final List> getObjectWriters(boolean forMultipart) { if (!this.registerDefaults) { return Collections.emptyList(); } @@ -179,13 +207,22 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs { if (jaxb2Present) { writers.add(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder())); } - extendObjectWriters(writers); + // No client or server specific multipart writers currently.. + if (!forMultipart) { + extendObjectWriters(writers); + } return writers; } + /** + * Hook for client or server specific Object writers. + */ protected void extendObjectWriters(List> objectWriters) { } + /** + * Return writers that need to be at the end, after all others. + */ List> getCatchAllWriters() { if (!this.registerDefaults) { return Collections.emptyList(); diff --git a/spring-web/src/main/java/org/springframework/http/codec/support/ClientDefaultCodecsImpl.java b/spring-web/src/main/java/org/springframework/http/codec/support/ClientDefaultCodecsImpl.java index d98f9b24e0..cc5c0933b1 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/support/ClientDefaultCodecsImpl.java +++ b/spring-web/src/main/java/org/springframework/http/codec/support/ClientDefaultCodecsImpl.java @@ -29,7 +29,6 @@ import org.springframework.http.codec.HttpMessageWriter; import org.springframework.http.codec.ServerSentEventHttpMessageReader; import org.springframework.http.codec.multipart.MultipartHttpMessageWriter; import org.springframework.lang.Nullable; -import org.springframework.util.Assert; /** * Default implementation of {@link ClientCodecConfigurer.ClientDefaultCodecs}. @@ -45,18 +44,17 @@ class ClientDefaultCodecsImpl extends BaseDefaultCodecs implements ClientCodecCo private Decoder sseDecoder; @Nullable - private Supplier>> customTypedWriters; - - @Nullable - private Supplier>> customObjectWriters; + private Supplier>> partWritersSupplier; - void initCustomTypedWriters(Supplier>> supplier) { - this.customTypedWriters = supplier; - } - - void initCustomObjectWriters(Supplier>> supplier) { - this.customObjectWriters = supplier; + /** + * Set a supplier for part writers to use when + * {@link #multipartCodecs()} are not explicitly configured. + * That's the same set of writers as for general except for the multipart + * writer itself. + */ + void setPartWritersSupplier(Supplier>> supplier) { + this.partWritersSupplier = supplier; } @@ -86,36 +84,19 @@ class ClientDefaultCodecsImpl extends BaseDefaultCodecs implements ClientCodecCo @Override protected void extendTypedWriters(List> typedWriters) { - - MultipartHttpMessageWriter multipartWriter = new MultipartHttpMessageWriter( - resolvePartWriters(typedWriters, getObjectWriters()), new FormHttpMessageWriter()); - - typedWriters.add(multipartWriter); + typedWriters.add(new MultipartHttpMessageWriter(getPartWriters(), new FormHttpMessageWriter())); } - private List> resolvePartWriters(List> typedWriters, - List> objectWriters) { - - List> partWriters; - if (this.multipartCodecs != null) { - partWriters = this.multipartCodecs.getWriters(); - } - else { - Assert.notNull(this.customTypedWriters, "Expected custom typed writers supplier."); - Assert.notNull(this.customObjectWriters, "Expected custom object writers supplier."); - - partWriters = new ArrayList<>(typedWriters); - partWriters.addAll(this.customTypedWriters.get()); - - partWriters.addAll(objectWriters); - partWriters.addAll(this.customObjectWriters.get()); - - partWriters.addAll(super.getCatchAllWriters()); - } - return partWriters; + @SuppressWarnings("ConstantConditions") + private List> getPartWriters() { + return this.multipartCodecs != null ? + this.multipartCodecs.getWriters() : this.partWritersSupplier.get(); } + /** + * Default implementation of {@link ClientCodecConfigurer.MultipartCodecs}. + */ private static class DefaultMultipartCodecs implements ClientCodecConfigurer.MultipartCodecs { private final List> writers = new ArrayList<>(); @@ -137,4 +118,5 @@ class ClientDefaultCodecsImpl extends BaseDefaultCodecs implements ClientCodecCo return this.writers; } } + } diff --git a/spring-web/src/main/java/org/springframework/http/codec/support/DefaultClientCodecConfigurer.java b/spring-web/src/main/java/org/springframework/http/codec/support/DefaultClientCodecConfigurer.java index 51eee36629..e17b4a040e 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/support/DefaultClientCodecConfigurer.java +++ b/spring-web/src/main/java/org/springframework/http/codec/support/DefaultClientCodecConfigurer.java @@ -29,8 +29,7 @@ public class DefaultClientCodecConfigurer extends BaseCodecConfigurer implements public DefaultClientCodecConfigurer() { super(new ClientDefaultCodecsImpl()); - ((ClientDefaultCodecsImpl) defaultCodecs()).initCustomTypedWriters(getCustomTypedWriters()); - ((ClientDefaultCodecsImpl) defaultCodecs()).initCustomObjectWriters(getCustomObjectWriters()); + ((ClientDefaultCodecsImpl) defaultCodecs()).setPartWritersSupplier(() -> getWritersInternal(true)); } diff --git a/spring-web/src/test/java/org/springframework/http/codec/support/CodecConfigurerTests.java b/spring-web/src/test/java/org/springframework/http/codec/support/CodecConfigurerTests.java index 2fea3e57f2..d0a08fa94e 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/support/CodecConfigurerTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/support/CodecConfigurerTests.java @@ -37,6 +37,7 @@ import org.springframework.http.MediaType; import org.springframework.http.codec.CodecConfigurer; import org.springframework.http.codec.DecoderHttpMessageReader; import org.springframework.http.codec.EncoderHttpMessageWriter; +import org.springframework.http.codec.FormHttpMessageReader; import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.codec.HttpMessageWriter; import org.springframework.http.codec.ResourceHttpMessageWriter; @@ -66,12 +67,13 @@ public class CodecConfigurerTests { @Test public void defaultReaders() { List> readers = this.configurer.getReaders(); - assertEquals(9, readers.size()); + assertEquals(10, readers.size()); assertEquals(ByteArrayDecoder.class, getNextDecoder(readers).getClass()); assertEquals(ByteBufferDecoder.class, getNextDecoder(readers).getClass()); assertEquals(DataBufferDecoder.class, getNextDecoder(readers).getClass()); assertEquals(ResourceDecoder.class, getNextDecoder(readers).getClass()); assertStringDecoder(getNextDecoder(readers), true); + assertEquals(FormHttpMessageReader.class, readers.get(this.index.getAndIncrement()).getClass()); assertEquals(Jackson2JsonDecoder.class, getNextDecoder(readers).getClass()); assertEquals(Jackson2SmileDecoder.class, getNextDecoder(readers).getClass()); assertEquals(Jaxb2XmlDecoder.class, getNextDecoder(readers).getClass()); @@ -115,12 +117,13 @@ public class CodecConfigurerTests { List> readers = this.configurer.getReaders(); - assertEquals(13, readers.size()); + assertEquals(14, readers.size()); assertEquals(ByteArrayDecoder.class, getNextDecoder(readers).getClass()); assertEquals(ByteBufferDecoder.class, getNextDecoder(readers).getClass()); assertEquals(DataBufferDecoder.class, getNextDecoder(readers).getClass()); assertEquals(ResourceDecoder.class, getNextDecoder(readers).getClass()); assertEquals(StringDecoder.class, getNextDecoder(readers).getClass()); + assertEquals(FormHttpMessageReader.class, readers.get(this.index.getAndIncrement()).getClass()); assertSame(customDecoder1, getNextDecoder(readers)); assertSame(customReader1, readers.get(this.index.getAndIncrement())); assertEquals(Jackson2JsonDecoder.class, getNextDecoder(readers).getClass());