Skip to content

Commit decbb9c

Browse files
bclozelrstoyanchev
andcommitted
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 <[email protected]>
1 parent d1ab815 commit decbb9c

File tree

4 files changed

+100
-5
lines changed

4 files changed

+100
-5
lines changed

spring-web/src/main/java/org/springframework/http/codec/CodecConfigurer.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
package org.springframework.http.codec;
1818

1919
import java.util.List;
20+
import java.util.function.Consumer;
2021

2122
import org.springframework.core.codec.Decoder;
2223
import org.springframework.core.codec.Encoder;
24+
import org.springframework.lang.Nullable;
2325

2426
/**
2527
* Defines a common interface for configuring either client or server HTTP
@@ -212,6 +214,38 @@ interface CustomCodecs {
212214
* @param writer the writer to add
213215
*/
214216
void writer(HttpMessageWriter<?> writer);
217+
218+
/**
219+
* Register a callback for the {@link DefaultCodecConfig configuration}
220+
* applied to default codecs. This allows custom codecs to follow general
221+
* guidelines applied to default ones, such as logging details and limiting
222+
* the amount of buffered data.
223+
* @param codecsConfigConsumer the default codecs configuration callback
224+
* @since 5.1.12
225+
*/
226+
void withDefaultCodecConfig(Consumer<DefaultCodecConfig> codecsConfigConsumer);
227+
}
228+
229+
230+
/**
231+
* Common options applied to default codecs and passed in a callback to custom codecs
232+
* so they get a chance to align their behavior on the default ones.
233+
* @since 5.1.12
234+
*/
235+
interface DefaultCodecConfig {
236+
237+
/**
238+
* Get the configured limit on the number of bytes that can be buffered whenever
239+
* the input stream needs to be aggregated.
240+
*/
241+
@Nullable
242+
Integer maxInMemorySize();
243+
244+
/**
245+
* Whether to log form data at DEBUG level, and headers at TRACE level.
246+
* Both may contain sensitive information.
247+
*/
248+
boolean isEnableLoggingRequestDetails();
215249
}
216250

217251
}

spring-web/src/main/java/org/springframework/http/codec/support/BaseCodecConfigurer.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.ArrayList;
2020
import java.util.List;
21+
import java.util.function.Consumer;
2122

2223
import org.springframework.core.ResolvableType;
2324
import org.springframework.core.codec.Decoder;
@@ -39,6 +40,8 @@
3940
*/
4041
abstract class BaseCodecConfigurer implements CodecConfigurer {
4142

43+
protected boolean customCodecsInitialized;
44+
4245
protected final BaseDefaultCodecs defaultCodecs;
4346

4447
protected final DefaultCustomCodecs customCodecs;
@@ -88,6 +91,7 @@ public CustomCodecs customCodecs() {
8891

8992
@Override
9093
public List<HttpMessageReader<?>> getReaders() {
94+
initializeCustomCodecs();
9195
List<HttpMessageReader<?>> result = new ArrayList<>();
9296

9397
result.addAll(this.customCodecs.getTypedReaders());
@@ -113,6 +117,7 @@ public List<HttpMessageWriter<?>> getWriters() {
113117
* same except for the multipart writer itself.
114118
*/
115119
protected List<HttpMessageWriter<?>> getWritersInternal(boolean forMultipart) {
120+
initializeCustomCodecs();
116121
List<HttpMessageWriter<?>> result = new ArrayList<>();
117122

118123
result.addAll(this.customCodecs.getTypedWriters());
@@ -128,6 +133,13 @@ protected List<HttpMessageWriter<?>> getWritersInternal(boolean forMultipart) {
128133
@Override
129134
public abstract CodecConfigurer clone();
130135

136+
private void initializeCustomCodecs() {
137+
if(!this.customCodecsInitialized) {
138+
this.customCodecs.configConsumers.forEach(consumer -> consumer.accept(this.defaultCodecs));
139+
this.customCodecsInitialized = true;
140+
}
141+
}
142+
131143

132144
/**
133145
* Default implementation of {@code CustomCodecs}.
@@ -142,6 +154,7 @@ protected static final class DefaultCustomCodecs implements CustomCodecs {
142154

143155
private final List<HttpMessageWriter<?>> objectWriters = new ArrayList<>();
144156

157+
private final List<Consumer<DefaultCodecConfig>> configConsumers = new ArrayList<>();
145158

146159
DefaultCustomCodecs() {
147160
}
@@ -179,6 +192,11 @@ public void writer(HttpMessageWriter<?> writer) {
179192
(canWriteObject ? this.objectWriters : this.typedWriters).add(writer);
180193
}
181194

195+
@Override
196+
public void withDefaultCodecConfig(Consumer<DefaultCodecConfig> codecsConfigConsumer) {
197+
this.configConsumers.add(codecsConfigConsumer);
198+
}
199+
182200
// Package private accessors...
183201

184202
List<HttpMessageReader<?>> getTypedReaders() {

spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
* @author Rossen Stoyanchev
6161
* @author Sebastien Deleuze
6262
*/
63-
class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
63+
class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigurer.DefaultCodecConfig {
6464

6565
static final boolean jackson2Present;
6666

@@ -159,8 +159,9 @@ public void maxInMemorySize(int byteCount) {
159159
this.maxInMemorySize = byteCount;
160160
}
161161

162+
@Override
162163
@Nullable
163-
protected Integer maxInMemorySize() {
164+
public Integer maxInMemorySize() {
164165
return this.maxInMemorySize;
165166
}
166167

@@ -169,7 +170,8 @@ public void enableLoggingRequestDetails(boolean enable) {
169170
this.enableLoggingRequestDetails = enable;
170171
}
171172

172-
protected boolean isEnableLoggingRequestDetails() {
173+
@Override
174+
public boolean isEnableLoggingRequestDetails() {
173175
return this.enableLoggingRequestDetails;
174176
}
175177

src/docs/asciidoc/web/webflux.adoc

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -950,7 +950,7 @@ The following example shows how to do so for client-side requests:
950950
configurer.defaultCodecs().enableLoggingRequestDetails(true);
951951
952952
WebClient webClient = WebClient.builder()
953-
.exchangeStrategies(ExchangeStrategies.builder().codecs(consumer).build())
953+
.exchangeStrategies(strategies -> strategies.codecs(consumer))
954954
.build();
955955
----
956956
[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:
959959
val consumer: (ClientCodecConfigurer) -> Unit = { configurer -> configurer.defaultCodecs().enableLoggingRequestDetails(true) }
960960
961961
val webClient = WebClient.builder()
962-
.exchangeStrategies(ExchangeStrategies.builder().codecs(consumer).build())
962+
.exchangeStrategies({ strategies -> strategies.codecs(consumer) })
963963
.build()
964964
----
965965

966+
[[webflux-codecs-custom]]
967+
==== Custom codecs
966968

969+
Applications can register custom codecs for supporting additional media types,
970+
or specific behaviors that are not supported by the default codecs.
967971

972+
Some configuration options expressed by developers are enforced on default codecs.
973+
Custom codecs might want to get a chance to align with those preferences,
974+
like <<webflux-codecs-limits, enforcing buffering limits>>
975+
or <<webflux-logging-sensitive-data, logging sensitive data>>.
976+
977+
The following example shows how to do so for client-side requests:
978+
979+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
980+
.Java
981+
----
982+
Consumer<ClientCodecConfigurer> consumer = configurer -> {
983+
CustomDecoder customDecoder = new CustomDecoder();
984+
configurer.customCodecs().decoder(customDecoder);
985+
configurer.customCodecs().withDefaultCodecConfig(config ->
986+
customDecoder.maxInMemorySize(config.maxInMemorySize())
987+
);
988+
}
989+
990+
WebClient webClient = WebClient.builder()
991+
.exchangeStrategies(strategies -> strategies.codecs(consumer))
992+
.build();
993+
----
994+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
995+
.Kotlin
996+
----
997+
val consumer: (ClientCodecConfigurer) -> Unit = { configurer ->
998+
val customDecoder = CustomDecoder()
999+
configurer.customCodecs().decoder(customDecoder)
1000+
configurer.customCodecs().withDefaultCodecConfig({ config ->
1001+
customDecoder.maxInMemorySize(config.maxInMemorySize())
1002+
})
1003+
}
1004+
1005+
val webClient = WebClient.builder()
1006+
.exchangeStrategies({ strategies -> strategies.codecs(consumer) })
1007+
.build()
1008+
----
9681009

9691010
[[webflux-dispatcher-handler]]
9701011
== `DispatcherHandler`

0 commit comments

Comments
 (0)