Provide default codecs config callback to custom codecs
As a follow-up of gh-23961, this change provides a way for custom codecs to align with the default codecs' behavior on common features like buffer size limits and logging request details. Closes gh-24119 Co-authored-by: Rossen Stoyanchev <rstoyanchev@pivotal.io>
This commit is contained in:
@@ -17,9 +17,11 @@
|
||||
package org.springframework.http.codec;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.core.codec.Decoder;
|
||||
import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Defines a common interface for configuring either client or server HTTP
|
||||
@@ -213,6 +215,38 @@ public interface CodecConfigurer {
|
||||
* @param writer the writer to add
|
||||
*/
|
||||
void writer(HttpMessageWriter<?> writer);
|
||||
|
||||
/**
|
||||
* Register a callback for the {@link DefaultCodecConfig configuration}
|
||||
* applied to default codecs. This allows custom codecs to follow general
|
||||
* guidelines applied to default ones, such as logging details and limiting
|
||||
* the amount of buffered data.
|
||||
* @param codecsConfigConsumer the default codecs configuration callback
|
||||
* @since 5.1.12
|
||||
*/
|
||||
void withDefaultCodecConfig(Consumer<DefaultCodecConfig> codecsConfigConsumer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Common options applied to default codecs and passed in a callback to custom codecs
|
||||
* so they get a chance to align their behavior on the default ones.
|
||||
* @since 5.1.12
|
||||
*/
|
||||
interface DefaultCodecConfig {
|
||||
|
||||
/**
|
||||
* Get the configured limit on the number of bytes that can be buffered whenever
|
||||
* the input stream needs to be aggregated.
|
||||
*/
|
||||
@Nullable
|
||||
Integer maxInMemorySize();
|
||||
|
||||
/**
|
||||
* Whether to log form data at DEBUG level, and headers at TRACE level.
|
||||
* Both may contain sensitive information.
|
||||
*/
|
||||
boolean isEnableLoggingRequestDetails();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.http.codec.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Decoder;
|
||||
@@ -39,6 +40,8 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
abstract class BaseCodecConfigurer implements CodecConfigurer {
|
||||
|
||||
protected boolean customCodecsInitialized;
|
||||
|
||||
protected final BaseDefaultCodecs defaultCodecs;
|
||||
|
||||
protected final DefaultCustomCodecs customCodecs;
|
||||
@@ -88,6 +91,7 @@ abstract class BaseCodecConfigurer implements CodecConfigurer {
|
||||
|
||||
@Override
|
||||
public List<HttpMessageReader<?>> getReaders() {
|
||||
initializeCustomCodecs();
|
||||
List<HttpMessageReader<?>> result = new ArrayList<>();
|
||||
|
||||
result.addAll(this.defaultCodecs.getTypedReaders());
|
||||
@@ -113,6 +117,7 @@ abstract class BaseCodecConfigurer implements CodecConfigurer {
|
||||
* same except for the multipart writer itself.
|
||||
*/
|
||||
protected List<HttpMessageWriter<?>> getWritersInternal(boolean forMultipart) {
|
||||
initializeCustomCodecs();
|
||||
List<HttpMessageWriter<?>> result = new ArrayList<>();
|
||||
|
||||
result.addAll(this.defaultCodecs.getTypedWriters(forMultipart));
|
||||
@@ -128,6 +133,13 @@ abstract class BaseCodecConfigurer implements CodecConfigurer {
|
||||
@Override
|
||||
public abstract CodecConfigurer clone();
|
||||
|
||||
private void initializeCustomCodecs() {
|
||||
if(!this.customCodecsInitialized) {
|
||||
this.customCodecs.configConsumers.forEach(consumer -> consumer.accept(this.defaultCodecs));
|
||||
this.customCodecsInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Default implementation of {@code CustomCodecs}.
|
||||
@@ -142,6 +154,7 @@ abstract class BaseCodecConfigurer implements CodecConfigurer {
|
||||
|
||||
private final List<HttpMessageWriter<?>> objectWriters = new ArrayList<>();
|
||||
|
||||
private final List<Consumer<DefaultCodecConfig>> configConsumers = new ArrayList<>();
|
||||
|
||||
DefaultCustomCodecs() {
|
||||
}
|
||||
@@ -179,6 +192,10 @@ abstract class BaseCodecConfigurer implements CodecConfigurer {
|
||||
(canWriteObject ? this.objectWriters : this.typedWriters).add(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void withDefaultCodecConfig(Consumer<DefaultCodecConfig> codecsConfigConsumer) {
|
||||
this.configConsumers.add(codecsConfigConsumer);
|
||||
}
|
||||
|
||||
// Package private accessors...
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ import org.springframework.util.ClassUtils;
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
|
||||
class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigurer.DefaultCodecConfig {
|
||||
|
||||
static final boolean jackson2Present;
|
||||
|
||||
@@ -158,8 +158,9 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
|
||||
this.maxInMemorySize = byteCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Integer maxInMemorySize() {
|
||||
public Integer maxInMemorySize() {
|
||||
return this.maxInMemorySize;
|
||||
}
|
||||
|
||||
@@ -168,7 +169,8 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
|
||||
this.enableLoggingRequestDetails = enable;
|
||||
}
|
||||
|
||||
protected boolean isEnableLoggingRequestDetails() {
|
||||
@Override
|
||||
public boolean isEnableLoggingRequestDetails() {
|
||||
return this.enableLoggingRequestDetails;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.http.codec.support;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -336,6 +337,18 @@ public class CodecConfigurerTests {
|
||||
assertFalse(encoders.containsAll(Arrays.asList(jacksonEncoder, jaxb2Encoder, protoEncoder)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withDefaultCodecConfig() {
|
||||
AtomicBoolean callbackCalled = new AtomicBoolean(false);
|
||||
this.configurer.defaultCodecs().enableLoggingRequestDetails(true);
|
||||
this.configurer.customCodecs().withDefaultCodecConfig(config -> {
|
||||
assertTrue(config.isEnableLoggingRequestDetails());
|
||||
callbackCalled.compareAndSet(false, true);
|
||||
});
|
||||
this.configurer.getReaders();
|
||||
assertTrue(callbackCalled.get());
|
||||
}
|
||||
|
||||
private Decoder<?> getNextDecoder(List<HttpMessageReader<?>> readers) {
|
||||
HttpMessageReader<?> reader = readers.get(this.index.getAndIncrement());
|
||||
assertEquals(DecoderHttpMessageReader.class, reader.getClass());
|
||||
|
||||
@@ -883,16 +883,44 @@ The following example shows how to do so for client-side requests:
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
Consumer<ClientCodecConfigurer> consumer = configurer ->
|
||||
configurer.defaultCodecs().enableLoggingRequestDetails(true);
|
||||
configurer.defaultCodecs().enableLoggingRequestDetails(true);
|
||||
|
||||
WebClient webClient = WebClient.builder()
|
||||
.exchangeStrategies(ExchangeStrategies.builder().codecs(consumer).build())
|
||||
.build();
|
||||
.exchangeStrategies(strategies -> strategies.codecs(consumer))
|
||||
.build();
|
||||
----
|
||||
====
|
||||
|
||||
[[webflux-codecs-custom]]
|
||||
==== Custom codecs
|
||||
|
||||
Applications can register custom codecs for supporting additional media types,
|
||||
or specific behaviors that are not supported by the default codecs.
|
||||
|
||||
Some configuration options expressed by developers are enforced on default codecs.
|
||||
Custom codecs might want to get a chance to align with those preferences,
|
||||
like <<webflux-codecs-limits, enforcing buffering limits>>
|
||||
or <<webflux-logging-sensitive-data, logging sensitive data>>.
|
||||
|
||||
The following example shows how to do so for client-side requests:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
Consumer<ClientCodecConfigurer> consumer = configurer -> {
|
||||
CustomDecoder customDecoder = new CustomDecoder();
|
||||
configurer.customCodecs().decoder(customDecoder);
|
||||
configurer.customCodecs().withDefaultCodecConfig(config ->
|
||||
customDecoder.maxInMemorySize(config.maxInMemorySize())
|
||||
);
|
||||
}
|
||||
|
||||
WebClient webClient = WebClient.builder()
|
||||
.exchangeStrategies(strategies -> strategies.codecs(consumer))
|
||||
.build();
|
||||
----
|
||||
====
|
||||
|
||||
[[webflux-dispatcher-handler]]
|
||||
== `DispatcherHandler`
|
||||
|
||||
Reference in New Issue
Block a user