diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index ac556a0d..7f1c0280 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -70,6 +70,44 @@ ** xref:spring-cloud-gateway-server-mvc/glossary.adoc[] ** xref:spring-cloud-gateway-server-mvc/how-it-works.adoc[] ** xref:spring-cloud-gateway-server-mvc/java-routes-api.adoc[] +** xref:spring-cloud-gateway-server-mvc/gateway-request-predicates.adoc[] +** xref:spring-cloud-gateway-server-mvc/gateway-handler-filter-functions.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/addrequestheader.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/addrequestheadersifnotpresent.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/addrequestparameter.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/addresponseheader.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/circuitbreaker-filter.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/deduperesponseheader.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/fallback-headers.adoc[] +//*** xref:spring-cloud-gateway-server-mvc/filters/local-cache-response-filter.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/maprequestheader.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/modifyrequestbody.adoc[] +//*** xref:spring-cloud-gateway-server-mvc/filters/modifyresponsebody.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/prefixpath.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/preservehostheader.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/redirectto.adoc[] +//*** xref:spring-cloud-gateway-server-mvc/filters/removejsonattributesresponsebody.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/removerequestheader.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/removerequestparameter.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/removeresponseheader.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/requestheadersize.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/requestratelimiter.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/rewritelocationresponseheader.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/rewritepath.adoc[] +//*** xref:spring-cloud-gateway-server-mvc/filters/rewriterequestparameter.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/rewriteresponseheader.adoc[] +//*** xref:spring-cloud-gateway-server-mvc/filters/savesession.adoc[] +//*** xref:spring-cloud-gateway-server-mvc/filters/secureheaders.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/setpath.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/setrequestheader.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/setresponseheader.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/setstatus.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/stripprefix.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/retry.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/requestsize.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/setrequesthostheader.adoc[] +*** xref:spring-cloud-gateway-server-mvc/filters/tokenrelay.adoc[] +** xref:spring-cloud-gateway-server-mvc/writing-custom-predicates-and-filters.adoc[] // begin Gateway Proxy Exchange diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestheader.adoc new file mode 100644 index 00000000..855c57cc --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestheader.adoc @@ -0,0 +1,43 @@ +[[addrequestheader-filter]] += `AddRequestHeader` Filter + +The `AddRequestHeader` is a "before" filter that takes a `name` and `value` parameter. +The following example configures an `AddRequestHeader` filter: + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestHeader; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsAddReqHeader() { + return route(GET("/red"), http("https://example.org")) + .before(addRequestHeader("X-Request-red", "blue")); + } +} +---- + +This listing adds `X-Request-red:blue` header to the downstream request's headers for all matching requests. + +`AddRequestHeader` is aware of the URI variables used to match a path or host. +URI variables may be used in the value and are expanded at runtime. +The following example configures an `AddRequestHeader` filter that uses a variable: + +.GatewaySampleApplication.java +[source,java] +---- +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsAddReqHeader() { + return route(GET("/red/{segment}"), http("https://example.org")) + .before(addRequestHeader("X-Request-red", "blue-{segment}")); + } +} +---- diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestheadersifnotpresent.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestheadersifnotpresent.adoc new file mode 100644 index 00000000..dac8a933 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestheadersifnotpresent.adoc @@ -0,0 +1,62 @@ +[[addrequestheadersifnotpresent-filter]] += `AddRequestHeadersIfNotPresent` Filter + +The `AddRequestHeadersIfNotPresent` filter takes a collection of `name` and `value` pairs separated by colon. +The following example configures an `AddRequestHeadersIfNotPresent` filter: + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestHeadersIfNotPresent; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsAddReqHeader() { + return route(GET("/red"), http("https://example.org")) + .before(addRequestHeadersIfNotPresent("X-Request-Color-1:blue","X-Request-Color-2:green")); + } +} +---- + +This listing adds 2 headers `X-Request-Color-1:blue` and `X-Request-Color-2:green` to the downstream request's headers for all matching requests. +This is similar to how `AddRequestHeader` works, but unlike `AddRequestHeader` it will do it only if the header is not already there. +Otherwise, the original value in the client request is sent. + +Additionally, to set a multi-valued header, use the header name multiple times like `addRequestHeadersIfNotPresent("X-Request-Color-1:blue","X-Request-Color-1:green")`. + +`AddRequestHeadersIfNotPresent` also supports URI variables used to match a path or host. +URI variables may be used in the value and are expanded at runtime. +The following example configures an `AddRequestHeadersIfNotPresent` filter that uses a variable: + +.GatewaySampleApplication.java +[source,java] +---- +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsAddReqHeader() { + return route(GET("/red/{segment}"), http("https://example.org")) + .before(addRequestHeadersIfNotPresent("X-Request-red", "blue-{segment}")); + } +} +---- +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: add_request_header_route + uri: https://example.org + predicates: + - Path=/red/{segment} + filters: + - AddRequestHeadersIfNotPresent=X-Request-Red:Blue-{segment} +---- + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestparameter.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestparameter.adoc new file mode 100644 index 00000000..b41ba5c7 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestparameter.adoc @@ -0,0 +1,52 @@ +[[addrequestparameter-filter]] += `AddRequestParameter` Filter + +The `AddRequestParameter` Filter takes a `name` and `value` parameter. +The following example configures an `AddRequestParameter` filter: + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestParameter; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsAddReqHeader() { + return route("add_request_parameter_route") + .GET("/anything/addrequestparam", http("https://example.org")) + .before(addRequestParameter("red", "blue")) + .build(); + } +} +---- + +This will add `red=blue` to the downstream request's query string for all matching requests. + +`AddRequestParameter` is aware of the URI variables used to match a path or host. +URI variables may be used in the value and are expanded at runtime. +The following example configures an `AddRequestParameter` filter that uses a variable: + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestParameter; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; +import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.host; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsAddReqHeader() { + return route("add_request_parameter_route") + .route(host("{segment}.myhost.org"), http("https://example.org")) + .before(addRequestParameter("foo", "bar-{segment}")) + .build(); + } +} +---- \ No newline at end of file diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addresponseheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addresponseheader.adoc new file mode 100644 index 00000000..71224010 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addresponseheader.adoc @@ -0,0 +1,53 @@ +[[addresponseheader-filter]] += `AddResponseHeader` Filter + +The `AddResponseHeader` Filter takes a `name` and `value` parameter. +The following example configures an `AddResponseHeader` filter: + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.addResponseHeader; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsAddRespHeader() { + return route("addresponseheader") + .GET("/anything/addresheader", http("https://example.org")) + .after(addResponseHeader("X-Response-Red", "Blue")) + .build(); + } +} +---- + +This adds `X-Response-Red:Blue` header to the downstream response's headers for all matching requests. + +`AddResponseHeader` is aware of URI variables used to match a path or host. +URI variables may be used in the value and are expanded at runtime. +The following example configures an `AddResponseHeader` filter that uses a variable: + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.addResponseHeader; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; +import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.host; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsAddRespHeader() { + return route("add_response_header_route") + .route(host("{segment}.myhost.org"), http("https://example.org")) + .after(addResponseHeader("foo", "bar-{segment}")) + .build(); + } +} +---- + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/circuitbreaker-filter.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/circuitbreaker-filter.adoc new file mode 100644 index 00000000..dc50fde8 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/circuitbreaker-filter.adoc @@ -0,0 +1,210 @@ +[[spring-cloud-circuitbreaker-filter-factory]] += `CircuitBreaker` Filter + +The Spring Cloud CircuitBreaker GatewayFilter factory uses the Spring Cloud CircuitBreaker APIs to wrap Gateway routes in +a circuit breaker. Spring Cloud CircuitBreaker supports multiple libraries that can be used with Spring Cloud Gateway. Spring Cloud supports Resilience4J out of the box. + +To enable the Spring Cloud CircuitBreaker filter, you need to place `spring-cloud-starter-circuitbreaker-reactor-resilience4j` on the classpath. +The following example configures a Spring Cloud CircuitBreaker filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: circuitbreaker_route + uri: https://example.org + filters: + - CircuitBreaker=myCircuitBreaker +---- +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.CircuitBreakerFilterFunctions.circuitBreaker; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsCircuitBreakerNoFallback() { + return route("circuitbreakernofallback") + .route(path("/anything/circuitbreakernofallback"), http("https://example.org")) + .filter(circuitBreaker("mycb3")) + .build(); + } +} +---- + +To configure the circuit breaker, see the configuration for the underlying circuit breaker implementation you are using. + +* https://cloud.spring.io/spring-cloud-circuitbreaker/reference/html/spring-cloud-circuitbreaker.html[Resilience4J Documentation] + +The Spring Cloud CircuitBreaker filter can also accept an optional `fallbackUri` parameter. +Currently, only `forward:` schemed URIs are supported. +If the fallback is called, the request is forwarded to the controller matched by the URI. +The following example configures such a fallback: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: circuitbreaker_route + uri: https://example.org + predicates: + - Path=/consumingServiceEndpoint + filters: + - name: CircuitBreaker + args: + name: myCircuitBreaker + fallbackUri: forward:/inCaseOfFailureUseThis +---- + +The following listing does the same thing in Java: + +.GatewaySampleApplication.java +[source,java] +---- +import java.net.URI; +import static org.springframework.cloud.gateway.server.mvc.filter.CircuitBreakerFilterFunctions.circuitBreaker; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsCircuitBreakerFallback() { + return route("circuitbreaker_route") + .route(path("/consumingServiceEndpoint"), http("https://example.org")) + .filter(circuitBreaker("myCircuitBreaker", URI.create("forward:/inCaseOfFailureUseThis"))) + .build(); + } +} +---- + +This example forwards to the `/inCaseofFailureUseThis` URI when the circuit breaker fallback is called. + +CircuitBreaker also supports URI variables in the `fallbackUri`. +This allows more complex routing options, like forwarding sections of the original host or url path using https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/util/pattern/PathPattern.html[PathPattern expression]. + +In the example below the call `consumingServiceEndpoint/users/1` will be redirected to `inCaseOfFailureUseThis/users/1`. + +.GatewaySampleApplication.java +[source,java] +---- +import java.net.URI; +import static org.springframework.cloud.gateway.server.mvc.filter.CircuitBreakerFilterFunctions.circuitBreaker; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsCircuitBreakerFallback() { + return route("circuitbreaker_route") + .route(path("/consumingServiceEndpoint/{*segments}"), http("https://example.org")) + .filter(circuitBreaker("myCircuitBreaker", URI.create("forward:/inCaseOfFailureUseThis/{segments}"))) + .build(); + } +} +---- + + +The primary scenario is to use the `fallbackUri` to define an internal controller or handler within the gateway application. +However, you can also reroute the request to a controller or handler in an external application, as follows: + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.CircuitBreakerFilterFunctions.circuitBreaker; +import static org.springframework.cloud.gateway.server.mvc.filter.LoadBalancerFilterFunctions.lb; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsCircuitBreakerFallbackToGatewayRoute() { + return route("ingredients") + .route(path("/ingredients/**"), http()) + .filter(lb("ingredients")) + .filter(circuitBreaker("fetchIngredients", URI.create("forward:/fallback"))) + .build() + .and(route("ingredients-fallback") + .route(path("/fallback"), http("http://localhost:9994")) + .build()); + } +} +---- + +In this example, there is no `fallback` endpoint or handler in the gateway application. +However, there is one in another application, registered under `http://localhost:9994`. + +In case of the request being forwarded to fallback, the Spring Cloud CircuitBreaker Gateway filter also provides the `Throwable` that has caused it. +It is added to the `ServerRequest` as the `MvcUtils.CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR` attribute that can be used when handling the fallback within the gateway application. + +For the external controller/handler scenario, headers can be added with exception details. +You can find more information on doing so in the xref:spring-cloud-gateway-server-mvc/filters/fallback-headers.adoc[FallbackHeaders Filters section]. + +[[circuit-breaker-status-codes]] +== Tripping The Circuit Breaker On Status Codes + +In some cases you might want to trip a circuit breaker based on the status code +returned from the route it wraps. The circuit breaker config object takes a list of +status codes that if returned will cause the circuit breaker to be tripped. When setting the +status codes you want to trip the circuit breaker you can either use an integer with the status code +value or the String representation of the `HttpStatus` enumeration. + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: circuitbreaker_route + uri: lb://backing-service:8088 + predicates: + - Path=/consumingServiceEndpoint + filters: + - name: CircuitBreaker + args: + name: myCircuitBreaker + fallbackUri: forward:/inCaseOfFailureUseThis + statusCodes: + - 500 + - "NOT_FOUND" +---- + +.GatewaySampleApplication.java +[source,java] +---- +import java.net.URI; +import static org.springframework.cloud.gateway.server.mvc.filter.CircuitBreakerFilterFunctions.circuitBreaker; +import static org.springframework.cloud.gateway.server.mvc.filter.LoadBalancerFilterFunctions.lb; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsCircuitBreakerFallback() { + return route("circuitbreaker_route") + .route(path("/consumingServiceEndpoint"), http()) + .filter(lb("backing-service")) + .filter(circuitBreaker(config -> config.setId("myCircuitBreaker").setFallbackUri("forward:/inCaseOfFailureUseThis").setStatusCodes("500", "NOT_FOUND"))) + .build(); + } +} +---- + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/deduperesponseheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/deduperesponseheader.adoc new file mode 100644 index 00000000..8cb37c18 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/deduperesponseheader.adoc @@ -0,0 +1,46 @@ +[[deduperesponseheader-filter]] += `DedupeResponseHeader` Filter + +The `DedupeResponseHeader` GatewayFilter factory takes a `name` parameter and an optional `strategy` parameter. `name` can contain a space-separated list of header names. +The following example configures a `DedupeResponseHeader` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: dedupe_response_header_route + uri: https://example.org + filters: + - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin +---- +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.dedupeResponseHeader; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; +import static org.springframework.web.servlet.function.RequestPredicates.path; + + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsDedupeResponseHeader() { + return route("dedupe_response_header_route") + .route(path("/hello"), http("https://example.org")) + .after(dedupeResponseHeader("Access-Control-Allow-Credentials Access-Control-Allow-Origin")) + .build(); + } +} +---- + +This removes duplicate values of `Access-Control-Allow-Credentials` and `Access-Control-Allow-Origin` response headers in cases when both the gateway CORS logic and the downstream logic add them. + +The `DedupeResponseHeader` filter also accepts an optional `strategy` parameter. +The accepted values are `RETAIN_FIRST` (default), `RETAIN_LAST`, and `RETAIN_UNIQUE`. + + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/fallback-headers.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/fallback-headers.adoc new file mode 100644 index 00000000..4d541606 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/fallback-headers.adoc @@ -0,0 +1,70 @@ +[[fallback-headers]] += `FallbackHeaders` Filter + +The `FallbackHeaders` factory lets you add Spring Cloud CircuitBreaker execution exception details in the headers of a request forwarded to a `fallbackUri` in an external application, as in the following scenario: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: ingredients + uri: lb://ingredients + predicates: + - Path=//ingredients/** + filters: + - name: CircuitBreaker + args: + name: fetchIngredients + fallbackUri: forward:/fallback + - id: ingredients-fallback + uri: http://localhost:9994 + predicates: + - Path=/fallback + filters: + - name: FallbackHeaders + args: + executionExceptionTypeHeaderName: Test-Header +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.CircuitBreakerFilterFunctions.circuitBreaker; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.fallbackHeaders; +import static org.springframework.cloud.gateway.server.mvc.filter.LoadBalancerFilterFunctions.lb; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsCircuitBreakerFallbackToGatewayRoute() { + return route("ingredients") + .route(path("/ingredients/**"), http()) + .filter(lb("ingredients")) + .filter(circuitBreaker("fetchIngredients", URI.create("forward:/fallback"))) + .build() + .and(route("ingredients-fallback") + .route(path("/fallback"), http("http://localhost:9994")) + .before(fallbackHeaders()) + .build()); + } +} +---- + +In this example, after an execution exception occurs while running the circuit breaker, the request is forwarded to the `fallback` endpoint or handler in an application running on `localhost:9994`. +The headers with the exception type, message and (if available) root cause exception type and message are added to that request by the `FallbackHeaders` filter. + +You can overwrite the names of the headers in the configuration by setting the values of the following arguments (shown with their default values): + +* `executionExceptionTypeHeaderName` (`"Execution-Exception-Type"`) +* `executionExceptionMessageHeaderName` (`"Execution-Exception-Message"`) +* `rootCauseExceptionTypeHeaderName` (`"Root-Cause-Exception-Type"`) +* `rootCauseExceptionMessageHeaderName` (`"Root-Cause-Exception-Message"`) + +For more information on circuit breakers and the gateway see the xref:spring-cloud-gateway-server-mvc/filters/circuitbreaker-filter.adoc[Spring Cloud CircuitBreaker Filter section]. + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/local-cache-response-filter.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/local-cache-response-filter.adoc new file mode 100644 index 00000000..7cde3175 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/local-cache-response-filter.adoc @@ -0,0 +1,55 @@ +[[local-cache-response-filter]] += `LocalResponseCache` Filter + +This filter allows caching the response body and headers to follow these rules: + +* It can only cache bodiless GET requests. +* It caches the response only for one of the following status codes: HTTP 200 (OK), HTTP 206 (Partial Content), or HTTP 301 (Moved Permanently). +* Response data is not cached if `Cache-Control` header does not allow it (`no-store` present in the request or `no-store` or `private` present in the response). +* If the response is already cached and a new request is performed with no-cache value in `Cache-Control` header, it returns a bodiless response with 304 (Not Modified). + +This filter configures the local response cache per route and is available only if the `spring.cloud.gateway.filter.local-response-cache.enabled` property is enabled. And a xref:spring-cloud-gateway/global-filters.adoc#local-cache-response-global-filter[local response cache configured globally] is also available as feature. + +It accepts the first parameter to override the time to expire a cache entry (expressed in `s` for seconds, `m` for minutes, and `h` for hours) and a second parameter to set the maximum size of the cache to evict entries for this route (`KB`, `MB`, or `GB`). + +The following listing shows how to add local response cache filter: + +[source,java] +---- +@Bean +public RouteLocator routes(RouteLocatorBuilder builder) { + return builder.routes() + .route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org") + .filters(f -> f.prefixPath("/httpbin") + .localResponseCache(Duration.ofMinutes(30), "500MB") + ).uri(uri)) + .build(); +} +---- + +or this + +.application.yaml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: resource + uri: http://localhost:9000 + predicates: + - Path=/resource + filters: + - LocalResponseCache=30m,500MB +---- + +NOTE: This filter also automatically calculates the `max-age` value in the HTTP `Cache-Control` header. +Only if `max-age` is present on the original response is the value rewritten with the number of seconds set in the `timeToLive` configuration parameter. +In consecutive calls, this value is recalculated with the number of seconds left until the response expires. + +NOTE: To enable this feature, add `com.github.ben-manes.caffeine:caffeine` and `spring-boot-starter-cache` as project dependencies. + +WARNING: If your project creates custom `CacheManager` beans, it will either need to be marked with `@Primary` or injected using `@Qualifier`. + + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/maprequestheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/maprequestheader.adoc new file mode 100644 index 00000000..f44c572c --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/maprequestheader.adoc @@ -0,0 +1,31 @@ +[[maprequestheader-filter]] += `MapRequestHeader` Filter + +The `MapRequestHeader` filter takes `fromHeader` and `toHeader` parameters. +It creates a new named header (`toHeader`), and the value is extracted out of an existing named header (`fromHeader`) from the incoming http request. +If the input header does not exist, the filter has no impact. +If the new named header already exists, its values are augmented with the new values. +The following example configures a `MapRequestHeader`: + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestParameter; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsMapRequestHeader() { + return route("map_request_header_route") + .GET("/mypath", http("https://example.org")) + .before(mapRequestHeader("Blue", "X-Request-Red")) + .build(); + } +} +---- + +This adds the `X-Request-Red:` header to the downstream request with updated values from the incoming HTTP request's `Blue` header. + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/modifyrequestbody.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/modifyrequestbody.adoc new file mode 100644 index 00000000..963b26f0 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/modifyrequestbody.adoc @@ -0,0 +1,38 @@ +[[modifyrequestbody-filter]] += `ModifyRequestBody` Filter + +You can use the `ModifyRequestBody` filter to modify the request body before it is sent downstream by the gateway. + +NOTE: This filter can be configured only by using the Java DSL. + +The following listing shows how to modify a request body filter: + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.modifyRequestBody; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; +import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.host; +import org.springframework.http.MediaType; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsAddReqHeader() { + return route("rewrite_request_obj") + .route(host("*.rewriterequestobj.org"), http("https://example.org")) + .before(modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE, + (request, s) -> new Hello(s.toUpperCase()))) + .build(); + } + + record Hello(String message) { } +} +---- + +NOTE: If the request has no body, the `RewriteFilter` is passed `null`. `Mono.empty()` should be returned to assign a missing body in the request. + + + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/modifyresponsebody.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/modifyresponsebody.adoc new file mode 100644 index 00000000..f8211ed0 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/modifyresponsebody.adoc @@ -0,0 +1,46 @@ +[[modifyresponsebody-filter]] += `ModifyResponseBody` Filter + +You can use the `ModifyResponseBody` filter to modify the response body before it is sent back to the client. + +NOTE: This filter can be configured only by using the Java DSL. + +The following listing shows how to modify a response body filter: + +[source,java] +---- +@Bean +public RouteLocator routes(RouteLocatorBuilder builder) { + return builder.routes() + .route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org") + .filters(f -> f.prefixPath("/httpbin") + .modifyResponseBody(String.class, String.class, + (exchange, s) -> Mono.just(s.toUpperCase()))).uri(uri)) + .build(); +} +---- +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.modifyResponseBody; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; +import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.host; +import org.springframework.http.MediaType; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsAddReqHeader() { + return route("rewrite_request_obj") + .route(host("*.rewriterequestobj.org"), http("https://example.org")) + .before(modifyResponseBody(String.class, String.class, MediaType.APPLICATION_JSON_VALUE, (request, s) -> s.toUpperCase())) + .build(); + } + +} +---- + +NOTE: If the response has no body, the `RewriteFilter` is passed `null`. `Mono.empty()` should be returned to assign a missing body in the response. + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/prefixpath.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/prefixpath.adoc new file mode 100644 index 00000000..74ca85d5 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/prefixpath.adoc @@ -0,0 +1,43 @@ +[[prefixpath-filter]] += `PrefixPath` Filter +:page-section-summary-toc: 1 + +The `PrefixPath` filter takes a single `prefix` parameter. +The following example configures a `PrefixPath` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: prefixpath_route + uri: https://example.org + filters: + - PrefixPath=/mypath +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.prefixPath; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsPrefixPath() { + return route("prefixpath_route") + .GET("/**", http("https://example.org")) + .before("/mypath") + .build(); + } +} +---- + +This prefixes `/mypath` to the path of all matching requests. +So a request to `/hello` is sent to `/mypath/hello`. + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/preservehostheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/preservehostheader.adoc new file mode 100644 index 00000000..09c1e892 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/preservehostheader.adoc @@ -0,0 +1,42 @@ +[[preservehostheader-filter]] += `PreserveHostHeader` Filter +:page-section-summary-toc: 1 + +The `PreserveHostHeader` filter has no parameters. +This filter sets a request attribute that the `HandlerFunction` inspects to determine if the original host header should be sent rather than the host header determined by the HTTP client. +The following example configures a `PreserveHostHeader` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: preserve_host_route + uri: https://example.org + filters: + - PreserveHostHeader +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.preserveHostHeader; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsPreserveHostHeader() { + return route("preserve_host_route") + .GET("/**", http("https://example.org")) + .before(preserveHostHeader()) + .build(); + } +} +---- + + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/redirectto.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/redirectto.adoc new file mode 100644 index 00000000..8718b61b --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/redirectto.adoc @@ -0,0 +1,44 @@ +[[redirectto-filter]] += `RedirectTo` Filter + +The `RedirectTo` filter takes two parameters, `status` and `url`. +The `status` parameter should be a 300 series redirect HTTP code, such as 301. +The `url` parameter should be a valid URL. +This is the value of the `Location` header. +For relative redirects, you should use `uri: no://op` as the uri of your route definition. +The following listing configures a `RedirectTo` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: redirectto_route + uri: https://example.org + filters: + - RedirectTo=302, https://acme.org +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions.redirectTo; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsRedirectTo() { + return route("redirectto_route") + .GET("/**", http("https://example.org")) + .filter(redirectTo(302, URI.create("acme.org"))) + .build(); + } +} +---- + +This will send a status 302 with a `Location:https://acme.org` header to perform a redirect. diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removejsonattributesresponsebody.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removejsonattributesresponsebody.adoc new file mode 100644 index 00000000..922b5ffd --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removejsonattributesresponsebody.adoc @@ -0,0 +1,42 @@ +[[removejsonattributesresponsebody-filter]] += `RemoveJsonAttributesResponseBody` Filter + +The `RemoveJsonAttributesResponseBody` filter takes a collection of `attribute names` to search for, an optional last parameter from the list can be a boolean to remove the attributes just at root level (that's the default value if not present at the end of the parameter configuration, `false`) or recursively (`true`). +It provides a convenient method to apply a transformation to JSON body content by deleting attributes from it. + +The following example configures an `RemoveJsonAttributesResponseBody` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: removejsonattributes_route + uri: https://example.org + filters: + - RemoveJsonAttributesResponseBody=id,color +---- + +This removes attributes "id" and "color" from the JSON content body at root level. + +The following example configures an `RemoveJsonAttributesResponseBody` filter that uses the optional last parameter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: removejsonattributes_recursively_route + uri: https://example.org + predicates: + - Path=/red/{segment} + filters: + - RemoveJsonAttributesResponseBody=id,color,true +---- + +This removes attributes "id" and "color" from the JSON content body at any level. + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removerequestheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removerequestheader.adoc new file mode 100644 index 00000000..fc359a3e --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removerequestheader.adoc @@ -0,0 +1,43 @@ +[[removerequestheader-filter]] += `RemoveRequestHeader` GatewayFilter Factory +:page-section-summary-toc: 1 + +The `RemoveRequestHeader` filter takes a `name` parameter. +It is the name of the header to be removed. +The following listing configures a `RemoveRequestHeader` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: removerequestheader_route + uri: https://example.org + filters: + - RemoveRequestHeader=X-Request-Foo +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.removeRequestHeader; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsremoveRequestHeader() { + return route("removerequestheader_route") + .GET("/**", http("https://example.org")) + .before(removeRequestHeader("X-Request-Foo")) + .build(); + } +} +---- + +This removes the `X-Request-Foo` header before it is sent downstream. + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removerequestparameter.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removerequestparameter.adoc new file mode 100644 index 00000000..37c86f9e --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removerequestparameter.adoc @@ -0,0 +1,44 @@ +[[removerequestparameter-filter]] += `RemoveRequestParameter` Filter +:page-section-summary-toc: 1 + +The `RemoveRequestParameter` filter takes a `name` parameter. +It is the name of the query parameter to be removed. +The following example configures a `RemoveRequestParameter` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: removerequestparameter_route + uri: https://example.org + filters: + - RemoveRequestParameter=red +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestParameter; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsAddReqHeader() { + return route("removerequestparameter_route") + .GET("/**", http("https://example.org")) + .before(removeRequestParameter("red")) + .build(); + } +} +---- + +This will remove the `red` parameter before it is sent downstream. + + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removeresponseheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removeresponseheader.adoc new file mode 100644 index 00000000..6be39802 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removeresponseheader.adoc @@ -0,0 +1,46 @@ +[[removeresponseheader-filter]] += `RemoveResponseHeader` Filter + +The `RemoveResponseHeader` filter takes a `name` parameter. +It is the name of the header to be removed. +The following listing configures a `RemoveResponseHeader` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: removeresponseheader_route + uri: https://example.org + filters: + - RemoveResponseHeader=X-Response-Foo +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.removeResponseHeader; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsRemoveResponseHeader() { + return route("addresponseheader") + .GET("/anything/addresheader", http("https://example.org")) + .after(removeResponseHeader("X-Response-Foo")) + .build(); + } +} +---- + +This will remove the `X-Response-Foo` header from the response before it is returned to the gateway client. + +To remove any kind of sensitive header, you should configure this filter for any routes for which you may want to do so. +In addition, you can configure this filter once by using `spring.cloud.gateway.default-filters` and have it applied to all routes. + + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/requestheadersize.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/requestheadersize.adoc new file mode 100644 index 00000000..eb93af3a --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/requestheadersize.adoc @@ -0,0 +1,43 @@ +[[requestheadersize-filter]] += `RequestHeaderSize` Filter +:page-section-summary-toc: 1 + +The `RequestHeaderSize` filter takes `maxSize` and `errorHeaderName` parameters. +The `maxSize` parameter is the maximum data size allowed by the request header (including key and value). The `errorHeaderName` parameter sets the name of the response header containing an error message, by default it is "errorMessage". +The following listing configures a `RequestHeaderSize` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: requestheadersize_route + uri: https://example.org + filters: + - RequestHeaderSize=1000B +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.requestHeaderSize; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsRequestHeaderSize() { + return route("requestheadersize_route") + .GET("/**", http("https://example.org")) + .before(requestHeaderSize("1000B")) + .build(); + } +} +---- + +This will send a status 431 if size of any request header is greater than 1000 Bytes. + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/requestratelimiter.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/requestratelimiter.adoc new file mode 100644 index 00000000..6c05a083 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/requestratelimiter.adoc @@ -0,0 +1,120 @@ +[[requestratelimiter-filter]] += `RequestRateLimiter` Filter + +The `RequestRateLimiter` filter uses a `RateLimiter` implementation to determine if the current request is allowed to proceed. If it is not, a status of `HTTP 429 - Too Many Requests` (by default) is returned. + +This filter takes an optional `keyResolver` parameter and parameters specific to the rate limiter (described xref:spring-cloud-gateway/gatewayfilter-factories/requestratelimiter-factory.adoc#key-resolver-section[later in this section]). + +`keyResolver` is a bean that implements the `KeyResolver` interface. +In configuration, reference the bean by name using SpEL. +`#{@myKeyResolver}` is a SpEL expression that references a bean named `myKeyResolver`. +The following listing shows the `KeyResolver` interface: + +.KeyResolver.java +[source,java] +---- +public interface KeyResolver { + Mono resolve(ServerWebExchange exchange); +} +---- + +[[key-resolver-section]] +The `KeyResolver` interface lets pluggable strategies derive the key for limiting requests. +In future milestone releases, there will be some `KeyResolver` implementations. + +The default implementation of `KeyResolver` is the `PrincipalNameKeyResolver`, which retrieves the `Principal` from the `ServerWebExchange` and calls `Principal.getName()`. + +By default, if the `KeyResolver` does not find a key, requests are denied. +You can adjust this behavior by setting the `spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key` (`true` or `false`) and `spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code` properties. + +[NOTE] +===== +The `RequestRateLimiter` is not configurable with the "shortcut" notation. The following example below is _invalid_: + +.application.properties +---- +# INVALID SHORTCUT CONFIGURATION +spring.cloud.gateway.routes[0].filters[0]=RequestRateLimiter=2, 2, #{@userkeyresolver} +---- +===== + +[[redis-ratelimiter]] +== The Redis `RateLimiter` + +The Redis implementation is based on work done at https://stripe.com/blog/rate-limiters[Stripe]. +It requires the use of the `spring-boot-starter-data-redis-reactive` Spring Boot starter. + +The algorithm used is the https://en.wikipedia.org/wiki/Token_bucket[Token Bucket Algorithm]. + +The `redis-rate-limiter.replenishRate` property defines how many requests per second to allow (without any dropped requests). +This is the rate at which the token bucket is filled. + +The `redis-rate-limiter.burstCapacity` property is the maximum number of requests a user is allowed in a single second (without any dropped requests). +This is the number of tokens the token bucket can hold. +Setting this value to zero blocks all requests. + +The `redis-rate-limiter.requestedTokens` property is how many tokens a request costs. +This is the number of tokens taken from the bucket for each request and defaults to `1`. + +A steady rate is accomplished by setting the same value in `replenishRate` and `burstCapacity`. +Temporary bursts can be allowed by setting `burstCapacity` higher than `replenishRate`. +In this case, the rate limiter needs to be allowed some time between bursts (according to `replenishRate`), as two consecutive bursts results in dropped requests (`HTTP 429 - Too Many Requests`). +The following listing configures a `redis-rate-limiter`: + +Rate limits below `1 request/s` are accomplished by setting `replenishRate` to the wanted number of requests, `requestedTokens` to the timespan in seconds, and `burstCapacity` to the product of `replenishRate` and `requestedTokens`. +For example, setting `replenishRate=1`, `requestedTokens=60`, and `burstCapacity=60` results in a limit of `1 request/min`. +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: requestratelimiter_route + uri: https://example.org + filters: + - name: RequestRateLimiter + args: + redis-rate-limiter.replenishRate: 10 + redis-rate-limiter.burstCapacity: 20 + redis-rate-limiter.requestedTokens: 1 + +---- + +The following example configures a `KeyResolver` in Java: + +.Config.java +[source,java] +---- +@Bean +KeyResolver userKeyResolver() { + return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user")); +} +---- + +This defines a request rate limit of 10 per user. A burst of 20 is allowed, but, in the next second, only 10 requests are available. +The `KeyResolver` is a simple one that gets the `user` request parameter +NOTE: This is not recommended for production + +You can also define a rate limiter as a bean that implements the `RateLimiter` interface. +In configuration, you can reference the bean by name using SpEL. +`#{@myRateLimiter}` is a SpEL expression that references a bean with named `myRateLimiter`. +The following listing defines a rate limiter that uses the `KeyResolver` defined in the previous listing: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: requestratelimiter_route + uri: https://example.org + filters: + - name: RequestRateLimiter + args: + rate-limiter: "#{@myRateLimiter}" + key-resolver: "#{@userKeyResolver}" + +---- + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/requestsize.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/requestsize.adoc new file mode 100644 index 00000000..e5d398e7 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/requestsize.adoc @@ -0,0 +1,55 @@ +[[requestsize-filter]] += `RequestSize` Filter + +When the request size is greater than the permissible limit, the `RequestSize` filter can restrict a request from reaching the downstream service. +The filter takes a `maxSize` parameter. +The `maxSize` is a `DataSize` type, so values can be defined as a number followed by an optional `DataUnit` suffix such as 'KB' or 'MB'. The default is 'B' for bytes. +It is the permissible size limit of the request defined in bytes. +The following listing configures a `RequestSize` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: request_size_route + uri: http://localhost:8080 + predicates: + - Path=/upload + filters: + - name: RequestSize + args: + maxSize: 5000000 +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.requestSize; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsRequestSize() { + return route("request_size_route") + .GET("/upload", http("http://localhost:8080")) + .before(requestSize("5000000")) + .build(); + } +} +---- + +The `RequestSize` filter sets the response status as `413 Payload Too Large` with an additional header `errorMessage` when the request is rejected due to size. The following example shows such an `errorMessage`: + +[source] +---- +errorMessage : Request size is larger than permissible limit. Request size is 6.0 MB where permissible limit is 5.0 MB +---- + +NOTE: The default request size is set to five MB if not provided as a filter argument in the route definition. + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/retry.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/retry.adoc new file mode 100644 index 00000000..48a820cc --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/retry.adoc @@ -0,0 +1,86 @@ +[[retry-filter]] += `Retry` Filter + +The `Retry` filter supports the following parameters: + +* `retries`: The number of retries that should be attempted. +* `statuses`: The HTTP status codes that should be retried, represented by using `org.springframework.http.HttpStatus`. +* `methods`: The HTTP methods that should be retried, represented by using `org.springframework.http.HttpMethod`. +* `series`: The series of status codes to be retried, represented by using `org.springframework.http.HttpStatus.Series`. +* `exceptions`: A list of thrown exceptions that should be retried. +* `backoff`: The configured exponential backoff for the retries. +Retries are performed after a backoff interval of `firstBackoff * (factor ^ n)`, where `n` is the iteration. +If `maxBackoff` is configured, the maximum backoff applied is limited to `maxBackoff`. +If `basedOnPreviousValue` is true, the backoff is calculated by using `prevBackoff * factor`. + +The following defaults are configured for `Retry` filter, if enabled: + +* `retries`: Three times +* `series`: 5XX series +* `methods`: GET method +* `exceptions`: `IOException` and `TimeoutException` +* `backoff`: disabled + +The following listing configures a Retry filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: retry_test + uri: http://localhost:8080/flakey + predicates: + - Host=*.retry.com + filters: + - name: Retry + args: + retries: 3 + statuses: BAD_GATEWAY + methods: GET,POST + backoff: + firstBackoff: 10ms + maxBackoff: 50ms + factor: 2 + basedOnPreviousValue: false +---- + +NOTE: When using the retry filter with a `forward:` prefixed URL, the target endpoint should be written carefully so that, in case of an error, it does not do anything that could result in a response being sent to the client and committed. +For example, if the target endpoint is an annotated controller, the target controller method should not return `ResponseEntity` with an error status code. +Instead, it should throw an `Exception` or signal an error (for example, through a `Mono.error(ex)` return value), which the retry filter can be configured to handle by retrying. + +WARNING: When using the retry filter with any HTTP method with a body, the body will be cached and the gateway will become memory constrained. The body is cached in a request attribute defined by `ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR`. The type of the object is `org.springframework.core.io.buffer.DataBuffer`. + +A simplified "shortcut" notation can be added with a single `status` and `method`. + +The following two examples are equivalent: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: retry_route + uri: https://example.org + filters: + - name: Retry + args: + retries: 3 + statuses: INTERNAL_SERVER_ERROR + methods: GET + backoff: + firstBackoff: 10ms + maxBackoff: 50ms + factor: 2 + basedOnPreviousValue: false + + - id: retryshortcut_route + uri: https://example.org + filters: + - Retry=3,INTERNAL_SERVER_ERROR,GET,10ms,50ms,2,false +---- + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewritelocationresponseheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewritelocationresponseheader.adoc new file mode 100644 index 00000000..d31ccb3b --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewritelocationresponseheader.adoc @@ -0,0 +1,58 @@ +[[rewritelocationresponseheader-filter]] += `RewriteLocationResponseHeader` Filter + +The `RewriteLocationResponseHeader` filter modifies the value of the `Location` response header, usually to get rid of backend-specific details. +It takes the `stripVersionMode`, `locationHeaderName`, `hostValue`, and `protocolsRegex` parameters. +The following listing configures a `RewriteLocationResponseHeader` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: rewritelocationresponseheader_route + uri: http://example.org + filters: + - RewriteLocationResponseHeader=AS_IN_REQUEST, Location, , +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.addResponseHeader; +import static org.springframework.cloud.gateway.server.mvc.filter.RewriteLocationResponseHeaderFilterFunctions.StripVersion; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsRewriteLocationResponseHeader() { + return route("rewritelocationresponseheader_route") + .GET("/**", http("https://example.org")) + .after(rewriteLocationResponseHeader(config -> config.setLocationHeaderName("Location").setStripVersion(StripVersion.AS_IN_REQUEST))) + .build(); + } +} +---- + + + +For example, for a request of `POST https://api.example.com/some/object/name`, the `Location` response header value of `https://object-service.prod.example.net/v2/some/object/id` is rewritten as `https://api.example.com/some/object/id`. + +The `stripVersionMode` parameter has the following possible values: `NEVER_STRIP`, `AS_IN_REQUEST` (default), and `ALWAYS_STRIP`. + +* `NEVER_STRIP`: The version is not stripped, even if the original request path contains no version. +* `AS_IN_REQUEST`: The version is stripped only if the original request path contains no version. +* `ALWAYS_STRIP`: The version is always stripped, even if the original request path contains version. + +The `hostValue` parameter, if provided, is used to replace the `host:port` portion of the response `Location` header. +If it is not provided, the value of the `Host` request header is used. + +The `protocolsRegex` parameter must be a valid regex `String`, against which the protocol name is matched. +If it is not matched, the filter does nothing. +The default is `http|https|ftp|ftps`. + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewritepath.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewritepath.adoc new file mode 100644 index 00000000..6e40e259 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewritepath.adoc @@ -0,0 +1,44 @@ +[[rewritepath-filter]] += `RewritePath` Filter + +The `RewritePath` filter takes a path `regexp` parameter and a `replacement` parameter. +This uses Java regular expressions for a flexible way to rewrite the request path. +The following listing configures a `RewritePath` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: rewritepath_route + uri: https://example.org + predicates: + - Path=/red/** + filters: + - RewritePath=/red/?(?.*), /$\{segment} +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.rewritePath; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsRewritePath() { + return route("rewritepath_route") + .GET("/red/**", http("https://example.org")) + .before(rewritePath("/red/(?.*)", "/${segment}")) + .build(); + } +} +---- + +For a request path of `/red/blue`, this sets the path to `/blue` before making the downstream request. Note that in `application.yml` the `$` should be replaced with `$\` because of the YAML specification. + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewriterequestparameter.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewriterequestparameter.adoc new file mode 100644 index 00000000..13bc3780 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewriterequestparameter.adoc @@ -0,0 +1,44 @@ += `RewriteRequestParameter` Filter + +The `RewriteRequestParameter` filter takes a `name` parameter and a `replacement` parameter. +It will rewrite the value of the request parameter of the given `name`. +If multiple request parameters with the same `name` are set, they will be replaced with a single value. +If no request parameter is found, no changes will be made. +The following listing configures a `RewriteRequestParameter` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: rewriterequestparameter_route + uri: https://example.org + predicates: + - Path=/products + filters: + - RewriteRequestParameter=campaign,fall2023 +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestParameter; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsAddReqHeader() { + return route("add_request_parameter_route") + .GET("/anything/addrequestparam", http("https://example.org")) + .before(addRequestParameter("red", "blue")) + .build(); + } +} +---- + +For a request to `/products?campaign=old`, this sets the request parameter to `campaign=fall2023`. \ No newline at end of file diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewriteresponseheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewriteresponseheader.adoc new file mode 100644 index 00000000..bae7cfa2 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewriteresponseheader.adoc @@ -0,0 +1,44 @@ +[[rewriteresponseheader-filter]] += `RewriteResponseHeader` Filter +:page-section-summary-toc: 1 + +The `RewriteResponseHeader` filter takes `name`, `regexp`, and `replacement` parameters. +It uses Java regular expressions for a flexible way to rewrite the response header value. +The following example configures a `RewriteResponseHeader` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: rewriteresponseheader_route + uri: https://example.org + filters: + - RewriteResponseHeader=X-Response-Red, , password=[^&]+, password=*** +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.addResponseHeader; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsRewriteResponseHeader() { + return route("rewriteresponseheader_route") + .GET("/**", http("https://example.org")) + .after(rewriteResponseHeader("X-Request-Red", "password=[^&]+", "password=***")) + .build(); + } +} +---- + +For a header value of `/42?user=ford&password=omg!what&flag=true`, it is set to `/42?user=ford&password=\***&flag=true` after making the downstream request. +You must use `$\` to mean `$` because of the YAML specification in `application.yml`. + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/savesession.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/savesession.adoc new file mode 100644 index 00000000..35e10012 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/savesession.adoc @@ -0,0 +1,24 @@ +[[savesession-filter]] += `SaveSession` Filter + +The `SaveSession` filter forces a `WebSession::save` operation _before_ forwarding the call downstream. +This is of particular use when using something like https://projects.spring.io/spring-session/[Spring Session] with a lazy data store, and you need to ensure the session state has been saved before making the forwarded call. +The following example configures a `SaveSession` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: save_session + uri: https://example.org + predicates: + - Path=/foo/** + filters: + - SaveSession +---- + +If you integrate https://projects.spring.io/spring-security/[Spring Security] with Spring Session and want to ensure security details have been forwarded to the remote process, this is critical. + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/secureheaders.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/secureheaders.adoc new file mode 100644 index 00000000..9c2028ce --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/secureheaders.adoc @@ -0,0 +1,38 @@ +[[secureheaders-filter]] += `SecureHeaders` Filter + +The `SecureHeaders` filter adds a number of headers to the response, per the recommendation made in https://blog.appcanary.com/2017/http-security-headers.html[this blog post]. + +The following headers (shown with their default values) are added: + +* `X-Xss-Protection:1 (mode=block`) +* `Strict-Transport-Security (max-age=631138519`) +* `X-Frame-Options (DENY)` +* `X-Content-Type-Options (nosniff)` +* `Referrer-Policy (no-referrer)` +* `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)'` +* `X-Download-Options (noopen)` +* `X-Permitted-Cross-Domain-Policies (none)` + +To change the default values, set the appropriate property in the `spring.cloud.gateway.filter.secure-headers` namespace. +The following properties are available: + +* `xss-protection-header` +* `strict-transport-security` +* `frame-options` +* `content-type-options` +* `referrer-policy` +* `content-security-policy` +* `download-options` +* `permitted-cross-domain-policies` + +To disable the default values set the `spring.cloud.gateway.filter.secure-headers.disable` property with comma-separated values. +The following example shows how to do so: + +[source] +---- +spring.cloud.gateway.filter.secure-headers.disable=x-frame-options,strict-transport-security +---- + +NOTE: The lowercase full name of the secure header needs to be used to disable it.. + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setpath.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setpath.adoc new file mode 100644 index 00000000..b3241786 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setpath.adoc @@ -0,0 +1,46 @@ +[[setpath-filter]] += `SetPath` Filter + +The `SetPath` filter takes a path `template` parameter. +It offers a simple way to manipulate the request path by allowing templated segments of the path. +This uses the URI templates from Spring Framework. +Multiple matching segments are allowed. +The following example configures a `SetPath` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: setpath_route + uri: https://example.org + predicates: + - Path=/red/{segment} + filters: + - SetPath=/{segment} +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.setPath; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsSetPath() { + return route("add_request_parameter_route") + .GET("/red/{segment}", http("https://example.org")) + .before(setPath("/{segment")) + .build(); + } +} +---- + +For a request path of `/red/blue`, this sets the path to `/blue` before making the downstream request. + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setrequestheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setrequestheader.adoc new file mode 100644 index 00000000..4b9091f7 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setrequestheader.adoc @@ -0,0 +1,81 @@ +[[setrequestheader-filter]] += `SetRequestHeader` Filter + +The `SetRequestHeader` filter takes `name` and `value` parameters. +The following listing configures a `SetRequestHeader` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: setrequestheader_route + uri: https://example.org + filters: + - SetRequestHeader=X-Request-Red, Blue +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.setRequestHostHeader; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsSetRequestHeader() { + return route("add_request_parameter_route") + .GET("/**", http("https://example.org")) + .before(setRequestHostHeader("X-Request-Red", "Blue")) + .build(); + } +} +---- + +This filter replaces (rather than adding) all headers with the given name. +So, if the downstream server responded with `X-Request-Red:1234`, it will be replaced with `X-Request-Red:Blue`, which is what the downstream service would receive. + +`SetRequestHeader` is aware of URI variables used to match a path or host. +URI variables may be used in the value and are expanded at runtime. +The following example configures an `SetRequestHeader` filter that uses a variable: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: setrequestheader_route + uri: https://example.org + predicates: + - Host: {segment}.myhost.org + filters: + - SetRequestHeader=X-Request-Red, Blue-{segment} +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.setRequestHostHeader; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; +import static import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.host; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsSetRequestHeader() { + return route("add_request_parameter_route") + .route(host("{segment}.myhost.org"), http("https://example.org")) + .before(setRequestHostHeader("X-Request-Red", "Blue-{segment}")) + .build(); + } +} +---- \ No newline at end of file diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setrequesthostheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setrequesthostheader.adoc new file mode 100644 index 00000000..fd3c49a3 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setrequesthostheader.adoc @@ -0,0 +1,47 @@ +[[setrequesthostheader-filter]] += `SetRequestHostHeader` Filter + +There are certain situation when the host header may need to be overridden. In this situation, the `SetRequestHostHeader` filter can replace the existing host header with a specified value. +The filter takes a `host` parameter. +The following listing configures a `SetRequestHostHeader` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: set_request_host_header_route + uri: http://localhost:8080 + predicates: + - Path=/headers + filters: + - name: SetRequestHostHeader + args: + host: example.org +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.setRequestHostHeader; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsSetRequestHostHeader() { + return route("set_request_host_header_route") + .GET("/headers", http("http://localhost:8080")) + .before(setRequestHostHeader("example.org")) + .build(); + } +} +---- + +The `SetRequestHostHeader` filter replaces the value of the host header with `example.org`. + + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setresponseheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setresponseheader.adoc new file mode 100644 index 00000000..a3d971c4 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setresponseheader.adoc @@ -0,0 +1,82 @@ +[[setresponseheader-filter]] += `SetResponseHeader` Filter + +The `SetResponseHeader` filter takes `name` and `value` parameters. +The following listing configures a `SetResponseHeader` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: setresponseheader_route + uri: https://example.org + filters: + - SetResponseHeader=X-Response-Red, Blue +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.setResponseHeader; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsSetResponseHeader() { + return route("addresponseheader") + .GET("/anything/addresheader", http("https://example.org")) + .after(setResponseHeader("X-Response-Red", "Blue")) + .build(); + } +} +---- + +This GatewayFilter replaces (rather than adding) all headers with the given name. +So, if the downstream server responded with `X-Response-Red:1234`, it will be replaced with `X-Response-Red:Blue`, which is what the gateway client would receive. + +`SetResponseHeader` is aware of URI variables used to match a path or host. +URI variables may be used in the value and will be expanded at runtime. +The following example configures an `SetResponseHeader` filter that uses a variable: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: setresponseheader_route + uri: https://example.org + predicates: + - Host: {segment}.myhost.org + filters: + - SetResponseHeader=foo, bar-{segment} +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.setResponseHeader; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; +import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.host; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsSetResponseHeader() { + return route("add_response_header_route") + .route(host("{segment}.myhost.org"), http("https://example.org")) + .after(setResponseHeader("foo", "bar-{segment}")) + .build(); + } +} +---- + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setstatus.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setstatus.adoc new file mode 100644 index 00000000..24d634df --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setstatus.adoc @@ -0,0 +1,67 @@ +[[setstatus-filter]] += `SetStatus` Filter + +The `SetStatus` filter takes a single parameter, `status`. +It must be a valid Spring `HttpStatus`. +It may be the integer value `404` or the string representation of the enumeration: `NOT_FOUND`. +The following listing configures a `SetStatus` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: setstatusstring_route + uri: https://example.org + filters: + - SetStatus=UNAUTHORIZED + - id: setstatusint_route + uri: https://example.org + filters: + - SetStatus=401 +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.setStatus; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsSetStatus() { + return route("setstatus_route") + .GET("/path1", http("https://example.org")) + // setStatus("UNAUTHORIZED") works as well + .after(setStatus(HttpStatus.UNAUTHORIZED)) + .build().and(route("setstatusint_route") + .GET("/path2", http("https://example.org")) + .after(setStatus("401")) + .build()); + } +} +---- + +In either case, the HTTP status of the response is set to 401. + +//// +TODO: support original status header +You can configure the `SetStatus` filter to return the original HTTP status code from the proxied request in a header in the response. +The header is added to the response if configured with the following property: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + set-status: + original-status-header-name: original-http-status +---- +//// + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/stripprefix.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/stripprefix.adoc new file mode 100644 index 00000000..e01c8d89 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/stripprefix.adoc @@ -0,0 +1,44 @@ +[[stripprefix-filter]] += `StripPrefix` Filter + +The `StripPrefix` filter takes one parameter, `parts`. +The `parts` parameter indicates the number of parts in the path to strip from the request before sending it downstream. +The following listing configures a `StripPrefix` filter: + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: nameRoot + uri: https://nameservice + predicates: + - Path=/name/** + filters: + - StripPrefix=2 +---- + +.GatewaySampleApplication.java +[source,java] +---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.stripPrefix; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + +@Configuration +class RouteConfiguration { + + @Bean + public RouterFunction gatewayRouterFunctionsStripPrefix() { + return route("nameRoot") + .GET("/name/**", http("https://example.org")) + .before(stripPrefix(2)) + .build(); + } +} +---- + +When a request is made through the gateway to `/name/blue/red`, the request made to `nameservice` looks like `https://nameservice/red`. + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/tokenrelay.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/tokenrelay.adoc new file mode 100644 index 00000000..1c4a43fd --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/tokenrelay.adoc @@ -0,0 +1,103 @@ +[[tokenrelay-filter]] += `TokenRelay` Filter + +A Token Relay is where an OAuth2 consumer acts as a Client and +forwards the incoming token to outgoing resource requests. The +consumer can be a pure Client (like an SSO application) or a Resource +Server. + +Spring Cloud Gateway can forward OAuth2 access tokens downstream to the services +it is proxying using the `TokenRelay` filter. + +The `TokenRelay` filter takes one optional parameter, `clientRegistrationId`. +The following example configures a `TokenRelay` filter: + +.App.java +[source,java] +---- + +@Bean +public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { + return builder.routes() + .route("resource", r -> r.path("/resource") + .filters(f -> f.tokenRelay("myregistrationid")) + .uri("http://localhost:9000")) + .build(); +} +---- + +or this + +.application.yaml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: resource + uri: http://localhost:9000 + predicates: + - Path=/resource + filters: + - TokenRelay=myregistrationid +---- + +The example above specifies a `clientRegistrationId`, which can be used to obtain and forward an OAuth2 access token for any available `ClientRegistration`. + +Spring Cloud Gateway can also forward the OAuth2 access token of the currently authenticated user `oauth2Login()` is used to authenticate the user. +To add this functionality to the gateway, you can omit the `clientRegistrationId` parameter like this: + +.App.java +[source,java] +---- + +@Bean +public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { + return builder.routes() + .route("resource", r -> r.path("/resource") + .filters(f -> f.tokenRelay()) + .uri("http://localhost:9000")) + .build(); +} +---- + +or this + +.application.yaml +[source,yaml] +---- +spring: + cloud: + gateway: + routes: + - id: resource + uri: http://localhost:9000 + predicates: + - Path=/resource + filters: + - TokenRelay= +---- + +and it will (in addition to logging the user in and grabbing a token) +pass the authentication token downstream to the services (in this case +`/resource`). + +To enable this for Spring Cloud Gateway add the following dependencies + +- `org.springframework.boot:spring-boot-starter-oauth2-client` + +How does it work? The {github-code}/src/main/java/org/springframework/cloud/gateway/security/TokenRelayGatewayFilterFactory.java[filter] +extracts an OAuth2 access token from the currently authenticated user for the provided `clientRegistrationId`. +If no `clientRegistrationId` is provided, the currently authenticated user's own access token (obtained during login) is used. +In either case, the extracted access token is placed in a request header for the downstream requests. + +For a full working sample see https://github.com/spring-cloud-samples/sample-gateway-oauth2login[this project]. + +NOTE: A `TokenRelayGatewayFilterFactory` bean will only be created if the proper `spring.security.oauth2.client.*` properties are set which will trigger creation of a `ReactiveClientRegistrationRepository` bean. + +NOTE: The default implementation of `ReactiveOAuth2AuthorizedClientService` used by `TokenRelayGatewayFilterFactory` +uses an in-memory data store. You will need to provide your own implementation `ReactiveOAuth2AuthorizedClientService` +if you need a more robust solution. + + diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/gateway-handler-filter-functions.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/gateway-handler-filter-functions.adoc new file mode 100644 index 00000000..3368025a --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/gateway-handler-filter-functions.adoc @@ -0,0 +1,16 @@ +[[gateway-handler-filter-functions]] += Gateway Handler Filter Functions + +[[beforefilterfunctions]] +== Before Filter Functions + +The WebMvc.fn API has the concept of a "before" filter function, a `java.util.Function` that only acts on the request. Many before filter functions are referenced in `org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions` as static factory methods. They are adapted to generic `org.springframework.web.servlet.function.HandlerFilterFunction` in `org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions`. Either may be used, but the more specific `BeforeFilterFunctions` is preferred, where possible, as it is more explicit. + +[[afterfilterfunctions]] +== After Filter Functions + +The WebMvc.fn API has the concept of a "after" filter function, a `java.util.BiFunction` that can modify the response. Many after filter functions are referenced in `org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions` as static factory methods. They are adapted to generic `org.springframework.web.servlet.function.HandlerFilterFunction` in `org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions`. Either may be used, but the more specific `AfterFilterFunctions` is preferred, where possible, as it is more explicit. + +== Advanced Filter Functions + +Some filters can not be classified as a simple before or after filter. These filters do work both prior to and after the proxy request has been made. Some of these include filters located in `BodyFilterFunctions`, `Bucket4jFilterFunctions, `CircuitBreakerFilterFunctions`, `LoadBalancerFilterFunctions`, `RetryFilterFunctions`, and `TokenRelayFilterFunctions`, all located in the `org.springframework.cloud.gateway.server.mvc.filter` package. \ No newline at end of file diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/gateway-request-predicates.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/gateway-request-predicates.adoc new file mode 100644 index 00000000..f4486074 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/gateway-request-predicates.adoc @@ -0,0 +1,4 @@ +[[gateway-request-predicates]] += Gateway Request Predicates + +TODO \ No newline at end of file diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/java-routes-api.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/java-routes-api.adoc index ba9ee6ec..27680cfa 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/java-routes-api.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/java-routes-api.adoc @@ -38,4 +38,9 @@ class SimpleGateway { return route("simple_route").GET("/get", http("https://httpbin.org")); } } ----- \ No newline at end of file +---- + +[[gateway-handlerfunctions]] +== Gateway MVC Handler Functions + +Various `RouterFunctions.Builder` methods require a `HandlerFunction`. To create a route that is proxied by the MVC Gateway, `HandlerFunction` implementations are supplied in `org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions`. The most basic is the `http()` `HandlerFunction`. If a `URI` is supplied as a parameter, that is the `URI` used as the downstream target for sending the HTTP requests (as seen in the example above). If no parameter is passed, the function looks for a `URI` in the `org.springframework.cloud.gateway.server.mvc.common.MvcUtils.GATEWAY_REQUEST_URL_ATTR` request attribute. This allows for dynamic targets such as load balancing to set the `URI`. \ No newline at end of file diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/writing-custom-predicates-and-filters.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/writing-custom-predicates-and-filters.adoc new file mode 100644 index 00000000..fbded1a8 --- /dev/null +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/writing-custom-predicates-and-filters.adoc @@ -0,0 +1,4 @@ +[[writing-custom-predicates-and-filters]] += Writing Custom Predicates and Filters + +TODO \ No newline at end of file