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-24118 Co-authored-by: Rossen Stoyanchev <rstoyanchev@pivotal.io>
This commit is contained in:
parent
d1ab81587c
commit
decbb9ccf9
|
@ -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
|
||||
|
@ -212,6 +214,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.customCodecs.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.customCodecs.getTypedWriters());
|
||||
|
@ -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,11 @@ 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...
|
||||
|
||||
List<HttpMessageReader<?>> getTypedReaders() {
|
||||
|
|
|
@ -60,7 +60,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;
|
||||
|
||||
|
@ -159,8 +159,9 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
|
|||
this.maxInMemorySize = byteCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Integer maxInMemorySize() {
|
||||
public Integer maxInMemorySize() {
|
||||
return this.maxInMemorySize;
|
||||
}
|
||||
|
||||
|
@ -169,7 +170,8 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
|
|||
this.enableLoggingRequestDetails = enable;
|
||||
}
|
||||
|
||||
protected boolean isEnableLoggingRequestDetails() {
|
||||
@Override
|
||||
public boolean isEnableLoggingRequestDetails() {
|
||||
return this.enableLoggingRequestDetails;
|
||||
}
|
||||
|
||||
|
|
|
@ -950,7 +950,7 @@ The following example shows how to do so for client-side requests:
|
|||
configurer.defaultCodecs().enableLoggingRequestDetails(true);
|
||||
|
||||
WebClient webClient = WebClient.builder()
|
||||
.exchangeStrategies(ExchangeStrategies.builder().codecs(consumer).build())
|
||||
.exchangeStrategies(strategies -> strategies.codecs(consumer))
|
||||
.build();
|
||||
----
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
|
@ -959,12 +959,53 @@ The following example shows how to do so for client-side requests:
|
|||
val consumer: (ClientCodecConfigurer) -> Unit = { configurer -> configurer.defaultCodecs().enableLoggingRequestDetails(true) }
|
||||
|
||||
val webClient = WebClient.builder()
|
||||
.exchangeStrategies(ExchangeStrategies.builder().codecs(consumer).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",role="primary"]
|
||||
.Java
|
||||
----
|
||||
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();
|
||||
----
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
val consumer: (ClientCodecConfigurer) -> Unit = { configurer ->
|
||||
val customDecoder = CustomDecoder()
|
||||
configurer.customCodecs().decoder(customDecoder)
|
||||
configurer.customCodecs().withDefaultCodecConfig({ config ->
|
||||
customDecoder.maxInMemorySize(config.maxInMemorySize())
|
||||
})
|
||||
}
|
||||
|
||||
val webClient = WebClient.builder()
|
||||
.exchangeStrategies({ strategies -> strategies.codecs(consumer) })
|
||||
.build()
|
||||
----
|
||||
|
||||
[[webflux-dispatcher-handler]]
|
||||
== `DispatcherHandler`
|
||||
|
|
Loading…
Reference in New Issue