diff --git a/spring-cloud-modules/spring-cloud-gateway/pom.xml b/spring-cloud-modules/spring-cloud-gateway/pom.xml
index 5d0594a259..18c306e61c 100644
--- a/spring-cloud-modules/spring-cloud-gateway/pom.xml
+++ b/spring-cloud-modules/spring-cloud-gateway/pom.xml
@@ -65,6 +65,14 @@
jakarta.validation-api
${jakarta.validation-api.version}
+
+ org.springframework.boot
+ spring-boot-starter-cache
+
+
+ com.github.ben-manes.caffeine
+ caffeine
+
org.springframework.boot
spring-boot-starter-actuator
@@ -179,11 +187,12 @@
+ 3.2.3
2023.0.0
8.0.1.Final
0.7.2
9.19
-
+ 5.10.2
3.0.2
diff --git a/spring-cloud-modules/spring-cloud-gateway/src/main/java/com/baeldung/springcloudgateway/webfilters/CacheEvaluationFilter.java b/spring-cloud-modules/spring-cloud-gateway/src/main/java/com/baeldung/springcloudgateway/webfilters/CacheEvaluationFilter.java
new file mode 100644
index 0000000000..bf23485402
--- /dev/null
+++ b/spring-cloud-modules/spring-cloud-gateway/src/main/java/com/baeldung/springcloudgateway/webfilters/CacheEvaluationFilter.java
@@ -0,0 +1,22 @@
+package com.baeldung.springcloudgateway.webfilters;
+
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
+import org.springframework.stereotype.Component;
+import org.springframework.web.server.ServerWebExchange;
+
+import reactor.core.publisher.Mono;
+
+@Component
+public class CacheEvaluationFilter implements GlobalFilter {
+
+ @Override
+ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+ String body = exchange.getAttribute(ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR);
+ if (body != null) {
+ exchange.getResponse().getHeaders().add("My-Header-Cache", body);
+ }
+ return chain.filter(exchange);
+ }
+}
diff --git a/spring-cloud-modules/spring-cloud-gateway/src/main/resources/application-webfilters.yml b/spring-cloud-modules/spring-cloud-gateway/src/main/resources/application-webfilters.yml
index 3348cbbba0..79aa8f3a04 100644
--- a/spring-cloud-modules/spring-cloud-gateway/src/main/resources/application-webfilters.yml
+++ b/spring-cloud-modules/spring-cloud-gateway/src/main/resources/application-webfilters.yml
@@ -9,6 +9,11 @@ spring:
port: 6379
cloud:
gateway:
+ filter:
+ local-response-cache:
+ enabled: true
+ timeToLive: 20m
+ size: 6MB
routes:
- id: request_header_route
uri: https://httpbin.org
@@ -17,6 +22,7 @@ spring:
filters:
- AddRequestHeader=My-Header-Good,Good
- AddRequestHeader=My-Header-Remove,Remove
+ - AddRequestHeadersIfNotPresent=My-Header-Absent:Absent
- AddRequestParameter=var, good
- AddRequestParameter=var2, remove
- MapRequestHeader=My-Header-Good, My-Header-Bad
@@ -31,17 +37,18 @@ spring:
predicates:
- Path=/header/post/**
filters:
- - AddResponseHeader=My-Header-Good,Good
- - AddResponseHeader=My-Header-Set,Good
- - AddResponseHeader=My-Header-Rewrite, password=12345678
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
- - AddResponseHeader=My-Header-Remove,Remove
- SetResponseHeader=My-Header-Set, Set
- RemoveResponseHeader=My-Header-Remove
- RewriteResponseHeader=My-Header-Rewrite, password=[^&]+, password=***
- RewriteLocationResponseHeader=AS_IN_REQUEST, Location, ,
+ - RemoveJsonAttributesResponseBody=form,Accept,true
+ - AddResponseHeader=My-Header-Good,Good
+ - AddResponseHeader=My-Header-Set,Good
+ - AddResponseHeader=My-Header-Remove,Remove
+ - AddResponseHeader=My-Header-Rewrite,password=12345678
- StripPrefix=1
-
+
- id: path_route
uri: https://httpbin.org
predicates:
@@ -88,7 +95,18 @@ spring:
maxBackoff: 50ms
factor: 2
basedOnPreviousValue: false
-
+
+ - id: circuitbreaker_route
+ uri: https://httpbin.org
+ predicates:
+ - Path=/status/504
+ filters:
+ - name: CircuitBreaker
+ args:
+ name: myCircuitBreaker
+ fallbackUri: forward:/anything
+ - RewritePath=/status/504, /anything
+
- id: request_rate_limiter
uri: https://httpbin.org
predicates:
@@ -99,4 +117,23 @@ spring:
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 5
- key-resolver: "#{@userKeyResolver}"
\ No newline at end of file
+ redis-rate-limiter.requestedTokens: 1
+ key-resolver: "#{@userKeyResolver}"
+
+ - id: cache_request_body_route
+ uri: https://httpbin.org
+ predicates:
+ - Path=/cache/post/**
+ filters:
+ - StripPrefix=1
+ - name: CacheRequestBody
+ args:
+ bodyClass: java.lang.String
+
+ - id: cache_response_body_route
+ uri: https://httpbin.org
+ predicates:
+ - Path=/cache/get/**
+ filters:
+ - StripPrefix=1
+ - LocalResponseCache=10s,20MB
diff --git a/spring-cloud-modules/spring-cloud-gateway/src/test/java/com/baeldung/springcloudgateway/webfilters/WebFilterFactoriesLiveTest.java b/spring-cloud-modules/spring-cloud-gateway/src/test/java/com/baeldung/springcloudgateway/webfilters/WebFilterFactoriesLiveTest.java
index 0caf92c2e8..1fc28b5251 100644
--- a/spring-cloud-modules/spring-cloud-gateway/src/test/java/com/baeldung/springcloudgateway/webfilters/WebFilterFactoriesLiveTest.java
+++ b/spring-cloud-modules/spring-cloud-gateway/src/test/java/com/baeldung/springcloudgateway/webfilters/WebFilterFactoriesLiveTest.java
@@ -1,11 +1,8 @@
package com.baeldung.springcloudgateway.webfilters;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import org.assertj.core.api.Condition;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.jupiter.api.BeforeEach;
@@ -23,9 +20,10 @@ import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.reactive.server.WebTestClient.ResponseSpec;
+import org.springframework.web.reactive.function.BodyInserters;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
-@ActiveProfiles("webfilters")
+@ActiveProfiles({"webfilters","nosecurity"})
public class WebFilterFactoriesLiveTest {
@LocalServerPort
@@ -55,6 +53,7 @@ public class WebFilterFactoriesLiveTest {
assertThat(headers.getString("My-Header-Good")).isEqualTo("Good");
assertThat(headers.getString("My-Header-Bad")).isEqualTo("Good");
assertThat(headers.getString("My-Header-Set")).isEqualTo("Set");
+ assertThat(headers.getString("My-Header-Absent")).isEqualTo("Absent");
assertTrue(headers.isNull("My-Header-Remove"));
JSONObject vars = json.getJSONObject("args");
assertThat(vars.getString("var")).isEqualTo("good");
@@ -75,7 +74,10 @@ public class WebFilterFactoriesLiveTest {
.expectHeader()
.valueEquals("My-Header-Good", "Good")
.expectHeader()
- .doesNotExist("My-Header-Remove");
+ .doesNotExist("My-Header-Remove")
+ .expectBody()
+ .jsonPath("$.headers.Accept").doesNotExist()
+ .jsonPath("form").doesNotExist();
}
@Test
@@ -133,4 +135,28 @@ public class WebFilterFactoriesLiveTest {
JSONObject json = new JSONObject(response.getBody());
assertThat(json.getString("url")).contains("anything");
}
+
+ @Test
+ public void whenCallCachePostThroughGateway_thenMyHeaderCacheIsSet() {
+ ResponseSpec response = client.post()
+ .uri("/cache/post")
+ .body(BodyInserters.fromValue("CachedBody"))
+ .exchange();
+
+ response.expectStatus()
+ .isOk()
+ .expectHeader()
+ .valueEquals("My-Header-Cache", "CachedBody");
+ }
+
+ @Test
+ public void whenCallCacheGetThroughGateway_thenCacheControlIsSet() {
+ ResponseSpec response = client.get()
+ .uri("/cache/get")
+ .exchange();
+
+ response.expectStatus().isOk()
+ .expectHeader()
+ .valueEquals("Cache-Control", "max-age=10");
+ }
}