[BAEL-6276] Exploring the New Filters on Spring Cloud Gateway (#16341)

* BAEL-6276: Fix the broken tests

* BAEL-6276: Add request header AddRequestHeadersIfNotPresent

* BAEL-6276: Add response header RemoveJsonAttributesResponseBody

* BAEL-6276: Example for CacheRequestBody

* BAEL-6276: Example for LocalResponseCache

* BAEL-6276: Add `redis-rate-limiter.requestedTokens` for `RequestRateLimiter`
This commit is contained in:
Forb Yuan 2024-04-19 01:23:33 +08:00 committed by GitHub
parent 60ce9ba95f
commit ed6d85a620
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 107 additions and 13 deletions

View File

@ -65,6 +65,14 @@
<artifactId>jakarta.validation-api</artifactId>
<version>${jakarta.validation-api.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
@ -179,11 +187,12 @@
</profiles>
<properties>
<spring-boot.version>3.2.3</spring-boot.version>
<spring-cloud-dependencies.version>2023.0.0</spring-cloud-dependencies.version>
<hibernate-validator.version>8.0.1.Final</hibernate-validator.version>
<redis.version>0.7.2</redis.version>
<oauth2-oidc-sdk.version>9.19</oauth2-oidc-sdk.version>
<!-- <junit-bom.version>5.5.2</junit-bom.version> -->
<junit-jupiter.version>5.10.2</junit-jupiter.version>
<jakarta.validation-api.version>3.0.2</jakarta.validation-api.version>
</properties>

View File

@ -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<Void> 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);
}
}

View File

@ -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}"
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

View File

@ -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");
}
}