diff --git a/docs/modules/ROOT/partials/_configprops.adoc b/docs/modules/ROOT/partials/_configprops.adoc index a8eed641..7a042b63 100644 --- a/docs/modules/ROOT/partials/_configprops.adoc +++ b/docs/modules/ROOT/partials/_configprops.adoc @@ -3,10 +3,10 @@ |spring.cloud.gateway.default-filters | | List of filter definitions that are applied to every route. |spring.cloud.gateway.discovery.locator.enabled | `+++false+++` | Flag that enables DiscoveryClient gateway integration. -|spring.cloud.gateway.discovery.locator.filters | | +|spring.cloud.gateway.discovery.locator.filters | | |spring.cloud.gateway.discovery.locator.include-expression | `+++true+++` | SpEL expression that will evaluate whether to include a service in gateway integration or not, defaults to: true. |spring.cloud.gateway.discovery.locator.lower-case-service-id | `+++false+++` | Option to lower case serviceId in predicates and filters, defaults to false. Useful with eureka when it automatically uppercases serviceId. so MYSERIVCE, would match /myservice/** -|spring.cloud.gateway.discovery.locator.predicates | | +|spring.cloud.gateway.discovery.locator.predicates | | |spring.cloud.gateway.discovery.locator.route-id-prefix | | The prefix for the routeId, defaults to discoveryClient.getClass().getSimpleName() + "_". Service Id will be appended to create the routeId. |spring.cloud.gateway.discovery.locator.url-expression | `+++'lb://'+serviceId+++` | SpEL expression that create the uri for each route, defaults to: 'lb://'+serviceId. |spring.cloud.gateway.enabled | `+++true+++` | Enables gateway functionality. @@ -20,7 +20,7 @@ |spring.cloud.gateway.filter.hystrix.enabled | `+++true+++` | Enables the hystrix filter. |spring.cloud.gateway.filter.json-to-grpc.enabled | `+++true+++` | Enables the JSON to gRPC filter. |spring.cloud.gateway.filter.local-response-cache.enabled | `+++false+++` | Enables the local-response-cache filter. -|spring.cloud.gateway.filter.local-response-cache.request.no-cache-strategy | `+++skip-update-cache-entry+++` | +|spring.cloud.gateway.filter.local-response-cache.request.no-cache-strategy | `+++skip-update-cache-entry+++` | |spring.cloud.gateway.filter.local-response-cache.size | | Maximum size of the cache to evict entries for this route (in KB, MB and GB). |spring.cloud.gateway.filter.local-response-cache.time-to-live | `+++5m+++` | Time to expire a cache entry (expressed in s for seconds, m for minutes, and h for hours). |spring.cloud.gateway.filter.map-request-header.enabled | `+++true+++` | Enables the map-request-header filter. @@ -29,15 +29,15 @@ |spring.cloud.gateway.filter.prefix-path.enabled | `+++true+++` | Enables the prefix-path filter. |spring.cloud.gateway.filter.preserve-host-header.enabled | `+++true+++` | Enables the preserve-host-header filter. |spring.cloud.gateway.filter.redirect-to.enabled | `+++true+++` | Enables the redirect-to filter. -|spring.cloud.gateway.filter.remove-hop-by-hop.headers | | -|spring.cloud.gateway.filter.remove-hop-by-hop.order | `+++0+++` | +|spring.cloud.gateway.filter.remove-hop-by-hop.headers | | +|spring.cloud.gateway.filter.remove-hop-by-hop.order | `+++0+++` | |spring.cloud.gateway.filter.remove-request-header.enabled | `+++true+++` | Enables the remove-request-header filter. |spring.cloud.gateway.filter.remove-request-parameter.enabled | `+++true+++` | Enables the remove-request-parameter filter. |spring.cloud.gateway.filter.remove-response-header.enabled | `+++true+++` | Enables the remove-response-header filter. |spring.cloud.gateway.filter.request-header-size.enabled | `+++true+++` | Enables the request-header-size filter. |spring.cloud.gateway.filter.request-header-to-request-uri.enabled | `+++true+++` | Enables the request-header-to-request-uri filter. -|spring.cloud.gateway.filter.request-rate-limiter.default-key-resolver | | -|spring.cloud.gateway.filter.request-rate-limiter.default-rate-limiter | | +|spring.cloud.gateway.filter.request-rate-limiter.default-key-resolver | | +|spring.cloud.gateway.filter.request-rate-limiter.default-rate-limiter | | |spring.cloud.gateway.filter.request-rate-limiter.enabled | `+++true+++` | Enables the request-rate-limiter filter. |spring.cloud.gateway.filter.request-size.enabled | `+++true+++` | Enables the request-size filter. |spring.cloud.gateway.filter.retry.enabled | `+++true+++` | Enables the retry filter. @@ -47,16 +47,16 @@ |spring.cloud.gateway.filter.rewrite-request-parameter.enabled | `+++true+++` | Enables the rewrite-request-parameter filter. |spring.cloud.gateway.filter.rewrite-response-header.enabled | `+++true+++` | Enables the rewrite-response-header filter. |spring.cloud.gateway.filter.save-session.enabled | `+++true+++` | Enables the save-session filter. -|spring.cloud.gateway.filter.secure-headers.content-security-policy | `+++default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'+++` | -|spring.cloud.gateway.filter.secure-headers.content-type-options | `+++nosniff+++` | -|spring.cloud.gateway.filter.secure-headers.disable | | -|spring.cloud.gateway.filter.secure-headers.download-options | `+++noopen+++` | +|spring.cloud.gateway.filter.secure-headers.content-security-policy | `+++default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'+++` | +|spring.cloud.gateway.filter.secure-headers.content-type-options | `+++nosniff+++` | +|spring.cloud.gateway.filter.secure-headers.disable | | +|spring.cloud.gateway.filter.secure-headers.download-options | `+++noopen+++` | |spring.cloud.gateway.filter.secure-headers.enabled | `+++true+++` | Enables the secure-headers filter. -|spring.cloud.gateway.filter.secure-headers.frame-options | `+++DENY+++` | -|spring.cloud.gateway.filter.secure-headers.permitted-cross-domain-policies | `+++none+++` | -|spring.cloud.gateway.filter.secure-headers.referrer-policy | `+++no-referrer+++` | -|spring.cloud.gateway.filter.secure-headers.strict-transport-security | `+++max-age=631138519+++` | -|spring.cloud.gateway.filter.secure-headers.xss-protection-header | `+++1 ; mode=block+++` | +|spring.cloud.gateway.filter.secure-headers.frame-options | `+++DENY+++` | +|spring.cloud.gateway.filter.secure-headers.permitted-cross-domain-policies | `+++none+++` | +|spring.cloud.gateway.filter.secure-headers.referrer-policy | `+++no-referrer+++` | +|spring.cloud.gateway.filter.secure-headers.strict-transport-security | `+++max-age=631138519+++` | +|spring.cloud.gateway.filter.secure-headers.xss-protection-header | `+++1 ; mode=block+++` | |spring.cloud.gateway.filter.set-path.enabled | `+++true+++` | Enables the set-path filter. |spring.cloud.gateway.filter.set-request-header.enabled | `+++true+++` | Enables the set-request-header filter. |spring.cloud.gateway.filter.set-request-host-header.enabled | `+++true+++` | Enables the set-request-host-header filter. @@ -76,7 +76,7 @@ |spring.cloud.gateway.global-filter.route-to-request-url.enabled | `+++true+++` | Enables the route-to-request-url global filter. |spring.cloud.gateway.global-filter.websocket-routing.enabled | `+++true+++` | Enables the websocket-routing global filter. |spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping | `+++false+++` | If global CORS config should be added to the URL handler. -|spring.cloud.gateway.globalcors.cors-configurations | | +|spring.cloud.gateway.globalcors.cors-configurations | | |spring.cloud.gateway.handler-mapping.order | `+++1+++` | The order of RoutePredicateHandlerMapping. |spring.cloud.gateway.httpclient.compression | `+++false+++` | Enables compression for Netty HttpClient. |spring.cloud.gateway.httpclient.connect-timeout | | The connect timeout in millis, the default is 30s. @@ -113,7 +113,7 @@ |spring.cloud.gateway.httpclient.websocket.proxy-ping | `+++true+++` | Proxy ping frames to downstream services, defaults to true. |spring.cloud.gateway.httpclient.wiretap | `+++false+++` | Enables wiretap debugging for Netty HttpClient. |spring.cloud.gateway.httpserver.wiretap | `+++false+++` | Enables wiretap debugging for Netty HttpServer. -|spring.cloud.gateway.loadbalancer.use404 | `+++false+++` | +|spring.cloud.gateway.loadbalancer.use404 | `+++false+++` | |spring.cloud.gateway.metrics.enabled | `+++false+++` | Enables the collection of metrics data. |spring.cloud.gateway.metrics.prefix | `+++spring.cloud.gateway+++` | The prefix of all metrics emitted by gateway. |spring.cloud.gateway.metrics.tags | | Tags map that added to metrics. @@ -161,7 +161,7 @@ |spring.cloud.gateway.predicate.weight.enabled | `+++true+++` | Enables the weight predicate. |spring.cloud.gateway.predicate.xforwarded-remote-addr.enabled | `+++true+++` | Enables the xforwarded-remote-addr predicate. |spring.cloud.gateway.redis-rate-limiter.burst-capacity-header | `+++X-RateLimit-Burst-Capacity+++` | The name of the header that returns the burst capacity configuration. -|spring.cloud.gateway.redis-rate-limiter.config | | +|spring.cloud.gateway.redis-rate-limiter.config | | |spring.cloud.gateway.redis-rate-limiter.include-headers | `+++true+++` | Whether or not to include headers containing rate limiter information, defaults to true. |spring.cloud.gateway.redis-rate-limiter.remaining-header | `+++X-RateLimit-Remaining+++` | The name of the header that returns number of remaining requests during the current second. |spring.cloud.gateway.redis-rate-limiter.replenish-rate-header | `+++X-RateLimit-Replenish-Rate+++` | The name of the header that returns the replenish rate configuration. @@ -172,7 +172,7 @@ |spring.cloud.gateway.route-refresh-listener.enabled | `+++true+++` | If RouteRefreshListener should be turned on. |spring.cloud.gateway.routes | | List of Routes. |spring.cloud.gateway.set-status.original-status-header-name | | The name of the header which contains http code of the proxied request. -|spring.cloud.gateway.streaming-media-types | | +|spring.cloud.gateway.streaming-media-types | | |spring.cloud.gateway.x-forwarded.enabled | `+++true+++` | If the XForwardedHeadersFilter is enabled. |spring.cloud.gateway.x-forwarded.for-append | `+++true+++` | If appending X-Forwarded-For as a list is enabled. |spring.cloud.gateway.x-forwarded.for-enabled | `+++true+++` | If X-Forwarded-For is enabled. @@ -186,4 +186,4 @@ |spring.cloud.gateway.x-forwarded.proto-append | `+++true+++` | If appending X-Forwarded-Proto as a list is enabled. |spring.cloud.gateway.x-forwarded.proto-enabled | `+++true+++` | If X-Forwarded-Proto is enabled. -|=== \ No newline at end of file +|=== diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractGatewayFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractGatewayFilterFactory.java index e94a2ede..2413fbda 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractGatewayFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractGatewayFilterFactory.java @@ -16,6 +16,7 @@ package org.springframework.cloud.gateway.filter.factory; +import org.springframework.cloud.gateway.event.EnableBodyCachingEvent; import org.springframework.cloud.gateway.support.AbstractConfigurable; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; @@ -43,6 +44,13 @@ public abstract class AbstractGatewayFilterFactory extends AbstractConfigurab return this.publisher; } + protected void enableBodyCaching(String routeId) { + if (routeId != null && getPublisher() != null) { + // send an event to enable caching + getPublisher().publishEvent(new EnableBodyCachingEvent(this, routeId)); + } + } + @Override public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { this.publisher = publisher; diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java index d8046e27..814a6ecf 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java @@ -35,7 +35,6 @@ import reactor.retry.RepeatContext; import reactor.retry.Retry; import reactor.retry.RetryContext; -import org.springframework.cloud.gateway.event.EnableBodyCachingEvent; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.support.HasRouteId; @@ -229,10 +228,7 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory repeat, Retry retry) { - if (routeId != null && getPublisher() != null) { - // send an event to enable caching - getPublisher().publishEvent(new EnableBodyCachingEvent(this, routeId)); - } + enableBodyCaching(routeId); return (exchange, chain) -> { trace("Entering retry-filter"); diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java index 0cbc3eca..823eac83 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java @@ -89,6 +89,9 @@ public abstract class SpringCloudCircuitBreakerFilterFactory @Override public GatewayFilter apply(Config config) { + if (config.getFallbackUri() != null) { + enableBodyCaching(config.getRouteId()); + } ReactiveCircuitBreaker cb = reactiveCircuitBreakerFactory.create(config.getId()); Set statuses = config.getStatusCodes() .stream() diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactoryTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactoryTests.java index 1966fa9e..30eb9a8b 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactoryTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactoryTests.java @@ -21,6 +21,7 @@ import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable; import org.springframework.cloud.gateway.test.BaseWebClientTests; import org.springframework.http.HttpStatus; +import org.springframework.web.reactive.function.BodyInserters; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.http.MediaType.APPLICATION_JSON; @@ -243,4 +244,11 @@ public abstract class SpringCloudCircuitBreakerFilterFactoryTests extends BaseWe .valueEquals(ROUTE_ID_HEADER, "circuitbreaker_resume_without_error"); } + @Test + public void filterPostFallback() { + testClient.post().uri("/post").body(BodyInserters.fromValue("hello")) + .header("Host", "www.circuitbreakerfallbackpost.org").exchange().expectStatus() + .isOk().expectBody().json("{\"body\":\"hello\"}"); + } + } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerTestConfig.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerTestConfig.java index 4487c19f..76a54365 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerTestConfig.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerTestConfig.java @@ -38,6 +38,8 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -68,6 +70,11 @@ public class SpringCloudCircuitBreakerTestConfig { return Collections.singletonMap("from", "circuitbreakerfallbackcontroller"); } + @PostMapping("/circuitbreakerPostFallbackController") + public Map postFallbackController(@RequestBody String body) { + return Collections.singletonMap("body", body); + } + @GetMapping("/circuitbreakerUriFallbackController/**") public Map uriFallbackcontroller(ServerWebExchange exchange, @RequestParam("a") String a) { return Collections.singletonMap("uri", exchange.getRequest().getURI().toString()); diff --git a/spring-cloud-gateway-server/src/test/resources/application.yml b/spring-cloud-gateway-server/src/test/resources/application.yml index d5798cdf..ac989a01 100644 --- a/spring-cloud-gateway-server/src/test/resources/application.yml +++ b/spring-cloud-gateway-server/src/test/resources/application.yml @@ -104,6 +104,19 @@ spring: name: fallbackcmd fallbackUri: forward:/circuitbreakerFallbackController + # ===================================== + - id: circuitbreaker_fallback_test_post + uri: ${test.uri} + predicates: + - Host=**.circuitbreakerfallbackpost.org + filters: + - name: CircuitBreaker + args: + name: fallbackcmd + statusCodes: + - 200 + fallbackUri: forward:/circuitbreakerPostFallbackController + # ===================================== - id: circuitbreaker_fallback_test_variables uri: ${test.uri}