Skip to content

Enable body filter codec encoding customization via CodecCustomizer#4151

Merged
ryanjbaxter merged 6 commits into
spring-cloud:mainfrom
qnnn:codec
May 21, 2026
Merged

Enable body filter codec encoding customization via CodecCustomizer#4151
ryanjbaxter merged 6 commits into
spring-cloud:mainfrom
qnnn:codec

Conversation

@qnnn
Copy link
Copy Markdown
Contributor

@qnnn qnnn commented Apr 17, 2026

This PR enables body filters to use codec encoders customized via CodecCustomizer.

Previously, body filters did not consistently apply configured codec customizations during encoding. As a result, encoding in some body filters could still fall back to default codec behavior instead of using the configured codec encoder customization.

This change ensures that body filter encoding also follows the configured codec customization, making codec behavior consistent between decoding and encoding.

@ryanjbaxter
Copy link
Copy Markdown
Contributor

Can you help me understand why you would want to switch the gateway from using Jackson 3 to Jackson 2?

@spencergibb
Copy link
Copy Markdown
Member

Gateway requires Jackson 3

@qnnn
Copy link
Copy Markdown
Contributor Author

qnnn commented May 7, 2026

@ryanjbaxter @spencergibb Thank you for your feedback.

I apologize that the PR described a very specific scenario, which may have made the issue seem narrower than it actually is. The underlying problem is likely more general — specifically, that the codec encoder cannot be configured consistently.

Our switch from Jackson3 back to Jackson2 is currently a transitional state during our upgrade process. While upgrading Gateway, we want to avoid any unexpected changes in the response body received by downstream callers (and similarly, avoid changes in the forwarded request body received by backend services). Since Jackson3 and Jackson2 have some differences in encoding behavior, these differences can be mitigated by enabling:

spring.jackson.use-jackson2-defaults=true

This allows us to use Jackson3 while still preserving Jackson2's default behavior.

So our actual goal is not necessarily to keep using Jackson2 itself, but rather to preserve the default serialization behavior that Jackson2 previously provided.

@ryanjbaxter
Copy link
Copy Markdown
Contributor

I apologize that the PR described a very specific scenario, which may have made the issue seem narrower than it actually is. The underlying problem is likely more general — specifically, that the codec encoder cannot be configured consistently.

So really this PR is enabling configuring the codec encoder vie CodecCustomizer? If so then please retitle the PR and update the description to reflect that.

Just to be clear setting spring.jackson.use-jackson2-defaults=true is not supported in Spring Cloud Gateway 5.0.x. However if the CodecCustomizer allows you to change Jackson 3 codec encoding to match the Jackson 2 codec encoding that seems reasonable.

@qnnn qnnn changed the title Ensure consistent Jackson codec usage in body filters Enable body filter codec encoding customization via CodecCustomizer May 7, 2026
@qnnn
Copy link
Copy Markdown
Contributor Author

qnnn commented May 7, 2026

@ryanjbaxter Thanks for the clarification. Yes, the core purpose of this PR is to make body filter encoding follow the codec configuration provided through CodecCustomizer.

I've updated the PR title and description to better reflect that focus.

@ryanjbaxter
Copy link
Copy Markdown
Contributor

Thanks.

We will need to add tests to the PR.

Also we cannot change public methods/constructors. We need to keep the existing ones in place, mark them as deprecated, and add new ones.

@qnnn
Copy link
Copy Markdown
Contributor Author

qnnn commented May 8, 2026

@ryanjbaxter I added some unit tests and found two behaviors that need clarification.

  • The unit test codecCustomizerWorksWithModifyRequestBody added in ModifyRequestBodyGatewayFilterFactoryTests could already pass before the fix. However, that does not mean there was no issue. In the same application context, the component that actually took effect was the WebFlux server rather than Gateway itself.

    More specifically, the request flow looked like this:

    1. request -> gateway
    2. gateway -> webflux server /post (same application context)
    3. codecCustomizer was applied by the WebFlux server layer

    As a result, the test passed because the WebFlux server handled the customization, not because Gateway itself correctly supported it.

  • FunctionRoutingFilter was also able to work correctly with codecCustomizer in previous versions because FunctionAroundWrapper already handled codecCustomizer properly at L132. For consistency, I also added codecCustomizer support for the subsequent bodyInserter processing at L152.

    Object functionResult = function.apply(inputMessage);
    if (functionResult instanceof Message message) {
    newResponseHeaders.addAll(MessageHeaderUtils.fromMessage(message.getHeaders(), ignoredHeaders));
    functionResult = message.getPayload();
    }
    Publisher result;
    if (functionResult instanceof Publisher<?> publisher) {
    // TODO: deal with eventStream
    result = publisher;
    }
    else {
    result = Mono.just(functionResult);
    }
    Class<?> outClass = byte[].class;
    BodyInserter bodyInserter = BodyInserters.fromPublisher(result, outClass);
    CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange,
    exchange.getResponse().getHeaders());
    return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {

@qnnn
Copy link
Copy Markdown
Contributor Author

qnnn commented May 9, 2026

Just to be clear setting spring.jackson.use-jackson2-defaults=true is not supported in Spring Cloud Gateway 5.0.x.

May I ask why spring.jackson.use-jackson2-defaults=true is considered unsupported in Spring Cloud Gateway 5.0.x? With the changes introduced by this PR, the configuration will take effect and effectively become supported.

Both CodecCustomizer and JsonMapperBuilderCustomizer can affect the behavior of codec encoders. Previously, I used:

spring.http.codecs.preferred-json-mapper=jackson2

to switch from Jackson 3 back to Jackson 2 directly.

Even when continuing to use Jackson 3, after this fix, configuring:

spring.jackson.use-jackson2-defaults=true

would also take effect and preserve Jackson 2-style defaults during encoding, since it is also applied through JsonMapperBuilderCustomizer.

@ryanjbaxter
Copy link
Copy Markdown
Contributor

We built and tested spring cloud gateway against the defaults of spring boot 4 which uses Jackson 3. We never guaranteed or test spring cloud gateway 5.0.x to be compatible with Jackson 2. So if you try it we cannot guarantee it will work, nor are we guaranteeing we will support any problems it may cause

@qnnn
Copy link
Copy Markdown
Contributor Author

qnnn commented May 15, 2026

We built and tested spring cloud gateway against the defaults of spring boot 4 which uses Jackson 3. We never guaranteed or test spring cloud gateway 5.0.x to be compatible with Jackson 2. So if you try it we cannot guarantee it will work, nor are we guaranteeing we will support any problems it may cause

Oh, Thanks for the clarification — I can see the concern more clearly now.

My understanding is that as long as Spring Cloud Gateway follows Spring Boot’s codec integration model consistently, the behavior itself should generally be guaranteed by Spring Boot.

At the moment, the issue seems to be that SCG follows the codec customization path during decode, but not consistently during encode. So from my perspective, this may be less about officially supporting Jackson 2, and more about keeping codec customization behavior consistent across decode and encode paths.

@ryanjbaxter
Copy link
Copy Markdown
Contributor

We are fine supporting the customizations but again I am not sure we are going to say we support Jackson 2 at this point

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates WebFlux body filter encoding paths to use codec customizations, aligning modified request/response/function routing body serialization with configured Spring Boot codec behavior.

Changes:

  • Adds CodecCustomizer-aware constructors and ExchangeStrategies usage for body inserter encoding.
  • Wires codec customizers through Gateway auto-configuration.
  • Adds integration tests covering codec customization for function routing, modify request body, and modify response body.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated no comments.

Show a summary per file
File Description
spring-cloud-gateway-server-webflux/src/main/java/org/springframework/cloud/gateway/support/BodyInserterContext.java Removes obsolete TODO now that custom strategies are supported.
spring-cloud-gateway-server-webflux/src/main/java/org/springframework/cloud/gateway/filter/FunctionRoutingFilter.java Applies customized exchange strategies when writing function responses.
spring-cloud-gateway-server-webflux/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyRequestBodyGatewayFilterFactory.java Applies customized exchange strategies when rewriting request bodies.
spring-cloud-gateway-server-webflux/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactory.java Applies customized exchange strategies when rewriting response bodies.
spring-cloud-gateway-server-webflux/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java Passes ordered codec customizers into body filter factories.
spring-cloud-gateway-server-webflux/src/main/java/org/springframework/cloud/gateway/config/GatewayFunctionAutoConfiguration.java Passes ordered codec customizers into function routing filter.
spring-cloud-gateway-server-webflux/src/test/java/org/springframework/cloud/gateway/test/TestCodecCustomizerConfiguration.java Adds shared test configuration for verifying custom JSON encoding.
spring-cloud-gateway-server-webflux/src/test/java/org/springframework/cloud/gateway/filter/FunctionRoutingFilterTests.java Adds coverage for customized encoding in function routing.
spring-cloud-gateway-server-webflux/src/test/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyRequestBodyGatewayFilterFactoryTests.java Adds coverage for customized encoding in request body rewriting.
spring-cloud-gateway-server-webflux/src/test/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactoryTests.java Adds coverage for customized encoding in response body rewriting.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@qnnn
Copy link
Copy Markdown
Contributor Author

qnnn commented May 18, 2026

Got it, we understand that Jackson 2 is already in a deprecated state, and in our case this is only a temporary transition scenario. We’ll gradually migrate fully to Jackson 3 as well.

* @author Junghoon Song
*/
@SpringBootTest(webEnvironment = RANDOM_PORT, properties = "spring.http.codecs.max-in-memory-size=13")
@SpringBootTest(webEnvironment = RANDOM_PORT, properties = "spring.http.codecs.max-in-memory-size=40")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this such a big increase?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the changes mentioned above based on the feedback. Please take a look.

Regarding the question in this comment:

The previous limit was too small, and the new tests I added also hit this limit.

My understanding is that the size restriction only needs to be low enough to trigger the test_modify_request_body_to_large test case. I also noticed that ModifyResponseBodyGatewayFilterFactoryTests uses 40, so I aligned the value here for consistency.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you might be able to set it to 15 and the tests will work, can you try that?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it worked. Thanks!

qnnn added 6 commits May 21, 2026 23:38
…coder.

Signed-off-by: qnnn <qiunan@cmbchina.com>
Signed-off-by: qnnn <qiunan@cmbchina.com>
Signed-off-by: qnnn <qiunan@cmbchina.com>
…ing'

Signed-off-by: qnnn <qiunan@cmbchina.com>
@ryanjbaxter
Copy link
Copy Markdown
Contributor

LGTM

@spencergibb mind taking another quick look?

@github-project-automation github-project-automation Bot moved this to Todo in 2025.1.2 May 21, 2026
@ryanjbaxter ryanjbaxter added this to the 5.0.2 milestone May 21, 2026
@ryanjbaxter ryanjbaxter merged commit 4231097 into spring-cloud:main May 21, 2026
2 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in 2025.1.2 May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants