From 2ebd776f1bec94d1480e417e5f4e293671fad0e5 Mon Sep 17 00:00:00 2001 From: spring-builds Date: Thu, 24 Apr 2025 13:27:51 +0000 Subject: [PATCH 1/2] Bumping versions --- .../cloud/gateway/filter/NettyRoutingFilterTests.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/NettyRoutingFilterTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/NettyRoutingFilterTests.java index b96bb9fa..b071212d 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/NettyRoutingFilterTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/NettyRoutingFilterTests.java @@ -113,7 +113,8 @@ class NettyRoutingFilterTests extends BaseWebClientTests { this.context.publishEvent(new EnvironmentChangeEvent(this.context, Collections.singleton("spring.cloud.gateway.httpclient.connect-timeout"))); Route route = routeLocator.getRoutes() - .filter(r -> r.getId().equals("refreshable_configuration_test")).blockLast(); + .filter(r -> r.getId().equals("refreshable_configuration_test")) + .blockLast(); assertThat(route).isNotNull(); HttpClient httpClient = nettyRoutingFilter.getHttpClient(route, null); HttpClientConfig configuration = httpClient.configuration(); From 24585036cee9c6fa63defa36bc1be22b36ab8ab8 Mon Sep 17 00:00:00 2001 From: raccoonback Date: Sat, 19 Apr 2025 16:45:02 +0900 Subject: [PATCH 2/2] Refactored the filter that removes fields from JSON response bodies factory Signed-off-by: raccoonback --- ...butesResponseBodyGatewayFilterFactory.java | 32 +++++-------- ...ResponseBodyGatewayFilterFactoryTests.java | 48 ++++++++++++++++--- 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveJsonAttributesResponseBodyGatewayFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveJsonAttributesResponseBodyGatewayFilterFactory.java index cb5b7d49..deea4c63 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveJsonAttributesResponseBodyGatewayFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveJsonAttributesResponseBodyGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 the original author or authors. + * Copyright 2013-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,6 @@ import java.util.List; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import reactor.core.publisher.Mono; @@ -34,6 +33,7 @@ import org.springframework.http.MediaType; /** * @author Marta Medio + * @author raccoonback */ public class RemoveJsonAttributesResponseBodyGatewayFilterFactory extends AbstractGatewayFilterFactory { @@ -72,14 +72,14 @@ public class RemoveJsonAttributesResponseBodyGatewayFilterFactory extends RewriteFunction rewriteFunction = (exchange, body) -> { if (MediaType.APPLICATION_JSON.isCompatibleWith(exchange.getResponse().getHeaders().getContentType())) { try { - JsonNode jsonBodyContent = mapper.readValue(body, JsonNode.class); + JsonNode jsonNode = mapper.readValue(body, JsonNode.class); - removeJsonAttribute(jsonBodyContent, config.getFieldList(), config.isDeleteRecursively()); + removeJsonAttributes(jsonNode, config.getFieldList(), config.isDeleteRecursively()); - body = mapper.writeValueAsString(jsonBodyContent); + body = mapper.writeValueAsString(jsonNode); } catch (JsonProcessingException e) { - throw new RuntimeException(e); + return Mono.error(new IllegalStateException("Failed to process JSON of response body.", e)); } } return Mono.just(body); @@ -93,22 +93,12 @@ public class RemoveJsonAttributesResponseBodyGatewayFilterFactory extends private ObjectMapper mapper = new ObjectMapper(); - private void removeJsonAttribute(JsonNode jsonBodyContent, List fieldsToRemove, boolean deleteRecursively) { - if (deleteRecursively) { - for (JsonNode jsonNode : jsonBodyContent) { - if (jsonNode instanceof ObjectNode) { - ((ObjectNode) jsonNode).remove(fieldsToRemove); - removeJsonAttribute(jsonNode, fieldsToRemove, true); - } - if (jsonNode instanceof ArrayNode) { - for (JsonNode node : jsonNode) { - removeJsonAttribute(node, fieldsToRemove, true); - } - } - } + private void removeJsonAttributes(JsonNode jsonNode, List fieldNames, boolean deleteRecursively) { + if (jsonNode instanceof ObjectNode objectNode) { + objectNode.remove(fieldNames); } - if (jsonBodyContent instanceof ObjectNode) { - ((ObjectNode) jsonBodyContent).remove(fieldsToRemove); + if (deleteRecursively) { + jsonNode.forEach(childNode -> removeJsonAttributes(childNode, fieldNames, true)); } } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RemoveJsonAttributesResponseBodyGatewayFilterFactoryTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RemoveJsonAttributesResponseBodyGatewayFilterFactoryTests.java index 12482d1a..228a1990 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RemoveJsonAttributesResponseBodyGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RemoveJsonAttributesResponseBodyGatewayFilterFactoryTests.java @@ -19,6 +19,7 @@ package org.springframework.cloud.gateway.filter.factory; import java.util.Map; import org.junit.jupiter.api.Test; +import reactor.core.publisher.Mono; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; @@ -30,8 +31,12 @@ import org.springframework.cloud.gateway.test.BaseWebClientTests; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @@ -42,10 +47,10 @@ import static org.springframework.cloud.gateway.test.TestUtils.getMap; */ @SpringBootTest(webEnvironment = RANDOM_PORT) @DirtiesContext -public class RemoveJsonAttributesResponseBodyGatewayFilterFactoryTests extends BaseWebClientTests { +class RemoveJsonAttributesResponseBodyGatewayFilterFactoryTests extends BaseWebClientTests { @Test - public void removeJsonAttributeRootWorks() { + void removeJsonAttributeRootWorks() { testClient.post() .uri("/post") .header("Host", "www.removejsonattributes.org") @@ -70,8 +75,7 @@ public class RemoveJsonAttributesResponseBodyGatewayFilterFactoryTests extends B } @Test - public void removeJsonAttributeRecursivelyWorks() { - + void removeJsonAttributeRecursivelyWorks() { testClient.post() .uri("/post") .header("Host", "www.removejsonattributesrecursively.org") @@ -93,8 +97,7 @@ public class RemoveJsonAttributesResponseBodyGatewayFilterFactoryTests extends B } @Test - public void removeJsonAttributeNoMatchesWorks() { - + void removeJsonAttributeNoMatchesWorks() { testClient.post() .uri("/post") .header("Host", "www.removejsonattributesnomatches.org") @@ -113,6 +116,21 @@ public class RemoveJsonAttributesResponseBodyGatewayFilterFactoryTests extends B }); } + @Test + void raisedWhenRemoveJsonAttributes() { + testClient.post() + .uri("/post") + .header("Host", "www.raisederrorwhenremovejsonattributes.org") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .exchange() + .expectStatus() + .is5xxServerError() + .expectBody(String.class) + .consumeWith(result -> { + assertThat(result.getResponseBody()).isEqualTo("Failed to process JSON of response body."); + }); + } + @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) @@ -142,9 +160,27 @@ public class RemoveJsonAttributesResponseBodyGatewayFilterFactoryTests extends B .host("{sub}.removejsonattributesnomatches.org") .filters(f -> f.removeJsonAttributes("test")) .uri(uri)) + .route("raised_error_when_remove_json_attributes", + r -> r.path("/post") + .and() + .host("{sub}.raisederrorwhenremovejsonattributes.org") + .filters(f -> f.removeJsonAttributes("test") + .modifyResponseBody(String.class, String.class, + (exchange, response) -> Mono.just("{\"invalid_json\": test"))) + .uri(uri)) .build(); } + @ControllerAdvice + public class GlobalExceptionHandler { + + @ExceptionHandler(IllegalStateException.class) + public ResponseEntity handleIllegalException(IllegalStateException ex) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.getMessage()); + } + + } + } }