Merge branch '4.1.x' into 4.2.x

This commit is contained in:
spencergibb
2025-03-28 15:08:04 -04:00
48 changed files with 754 additions and 270 deletions

View File

@@ -14,6 +14,8 @@ spring:
routes:
- id: add_request_header_route
uri: https://example.org
predicates:
- Path=/red
filters:
- AddRequestHeader=X-Request-red, blue
----
@@ -21,6 +23,7 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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;
@@ -30,8 +33,10 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsAddReqHeader() {
return route(GET("/red"), http("https://example.org"))
.before(addRequestHeader("X-Request-red", "blue"));
return route("add_request_header_route")
.GET("/red", http())
.before(uri("https://example.org"))
.before(addRequestHeader("X-Request-red", "blue"));
}
}
----
@@ -45,13 +50,20 @@ The following example configures an `AddRequestHeader` filter that uses a variab
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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<ServerResponse> gatewayRouterFunctionsAddReqHeader() {
return route(GET("/red/{segment}"), http("https://example.org"))
.before(addRequestHeader("X-Request-red", "blue-{segment}"));
return route("add_request_header_route")
.GET("/red/{segment}", http())
.before(uri("https://example.org"))
.before(addRequestHeader("X-Request-red", "blue-{segment}"));
}
}
----

View File

@@ -12,8 +12,10 @@ spring:
gateway:
mvc:
routes:
- id: add_request_headers_route
- id: add_request_headers_route_inp
uri: https://example.org
predicates:
- Path=/red
filters:
- AddRequestHeadersIfNotPresent=X-Request-Color-1:blue,X-Request-Color-2:green
----
@@ -21,6 +23,7 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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;
@@ -29,9 +32,11 @@ import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFuncti
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsAddReqHeader() {
return route(GET("/red"), http("https://example.org"))
.before(addRequestHeadersIfNotPresent("X-Request-Color-1:blue","X-Request-Color-2:green"));
public RouterFunction<ServerResponse> gatewayRouterFunctionsAddReqHeaderInp() {
return route("add_request_headers_route_inp")
.GET("/red", http())
.before(uri("https://example.org"))
.before(addRequestHeadersIfNotPresent("X-Request-Color-1:blue","X-Request-Color-2:green"));
}
}
----
@@ -49,13 +54,20 @@ The following example configures an `AddRequestHeadersIfNotPresent` filter that
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestHeadersIfNotPresent;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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<ServerResponse> gatewayRouterFunctionsAddReqHeader() {
return route(GET("/red/{segment}"), http("https://example.org"))
.before(addRequestHeadersIfNotPresent("X-Request-red", "blue-{segment}"));
public RouterFunction<ServerResponse> gatewayRouterFunctionsAddReqHeaderInp() {
return route("add_request_header_route_inp")
.GET("/red/{segment}", http())
.before(uri("https://example.org"))
.before(addRequestHeadersIfNotPresent("X-Request-red", "blue-{segment}"));
}
}
----
@@ -66,7 +78,7 @@ spring:
cloud:
gateway:
routes:
- id: add_request_header_route
- id: add_request_header_route_inp
uri: https://example.org
predicates:
- Path=/red/{segment}

View File

@@ -14,6 +14,8 @@ spring:
routes:
- id: add_request_parameter_route
uri: https://example.org
predicates:
- Path=/anything/addrequestparam
filters:
- AddRequestParameter=red, blue
----
@@ -21,6 +23,7 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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;
@@ -29,11 +32,12 @@ import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFuncti
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsAddReqHeader() {
return route("add_request_parameter_route")
.GET("/anything/addrequestparam", http("https://example.org"))
.before(addRequestParameter("red", "blue"))
.build();
public RouterFunction<ServerResponse> gatewayRouterFunctionsAddReqParameter() {
return route("add_request_parameter_route")
.GET("/anything/addrequestparam", http())
.before(uri("https://example.org"))
.before(addRequestParameter("red", "blue"))
.build();
}
}
----
@@ -47,6 +51,7 @@ The following example configures an `AddRequestParameter` filter that uses a var
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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;
@@ -56,11 +61,12 @@ import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequ
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsAddReqHeader() {
return route("add_request_parameter_route")
.route(host("{segment}.myhost.org"), http("https://example.org"))
.before(addRequestParameter("foo", "bar-{segment}"))
.build();
public RouterFunction<ServerResponse> gatewayRouterFunctionsAddReqParameter() {
return route("add_request_parameter_route")
.route(host("{segment}.myhost.org"), http())
.before(uri("https://example.org"))
.before(addRequestParameter("foo", "bar-{segment}"))
.build();
}
}
----

View File

@@ -14,6 +14,8 @@ spring:
routes:
- id: add_response_header_route
uri: https://example.org
predicates:
- Path=/anything/addresheader
filters:
- AddResponseHeader=X-Response-Red, Blue
----
@@ -21,6 +23,7 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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;
@@ -30,9 +33,10 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsAddRespHeader() {
return route("addresponseheader")
.GET("/anything/addresheader", http("https://example.org"))
.after(addResponseHeader("X-Response-Red", "Blue"))
return route("add_response_header_route")
.GET("/anything/addresheader", http())
.before(uri("https://example.org"))
.after(addResponseHeader("X-Response-Red", "Blue"))
.build();
}
}
@@ -47,6 +51,7 @@ The following example configures an `AddResponseHeader` filter that uses a varia
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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;
@@ -57,10 +62,11 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsAddRespHeader() {
return route("add_response_header_route")
.route(host("{segment}.myhost.org"), http("https://example.org"))
.after(addResponseHeader("foo", "bar-{segment}"))
.build();
return route("add_response_header_route")
.route(host("{segment}.myhost.org"), http())
.before(uri("https://example.org"))
.after(addResponseHeader("foo", "bar-{segment}"))
.build();
}
}
----

View File

@@ -15,14 +15,17 @@ spring:
gateway:
mvc:
routes:
- id: circuitbreaker_route
- id: circuitbreakernofallback
uri: https://example.org
predicates:
- Path=/anything/circuitbreakernofallback
filters:
- CircuitBreaker=myCircuitBreaker
----
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.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;
@@ -33,9 +36,10 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsCircuitBreakerNoFallback() {
return route("circuitbreakernofallback")
.route(path("/anything/circuitbreakernofallback"), http("https://example.org"))
.filter(circuitBreaker("mycb3"))
.build();
.route(path("/anything/circuitbreakernofallback"), http())
.before(uri("https://example.org"))
.filter(circuitBreaker("myCircuitBreaker"))
.build();
}
}
----
@@ -74,6 +78,7 @@ The following listing does the same thing in Java:
[source,java]
----
import java.net.URI;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.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;
@@ -84,9 +89,10 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsCircuitBreakerFallback() {
return route("circuitbreaker_route")
.route(path("/consumingServiceEndpoint"), http("https://example.org"))
.filter(circuitBreaker("myCircuitBreaker", URI.create("forward:/inCaseOfFailureUseThis")))
.build();
.route(path("/consumingServiceEndpoint"), http())
.before(uri("https://example.org"))
.filter(circuitBreaker("myCircuitBreaker", URI.create("forward:/inCaseOfFailureUseThis")))
.build();
}
}
----
@@ -102,6 +108,7 @@ In the example below the call `consumingServiceEndpoint/users/1` will be redirec
[source,java]
----
import java.net.URI;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.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;
@@ -112,9 +119,10 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsCircuitBreakerFallback() {
return route("circuitbreaker_route")
.route(path("/consumingServiceEndpoint/{*segments}"), http("https://example.org"))
.filter(circuitBreaker("myCircuitBreaker", URI.create("forward:/inCaseOfFailureUseThis/{segments}")))
.build();
.route(path("/consumingServiceEndpoint/{*segments}"), http())
.before(uri("https://example.org"))
.filter(circuitBreaker("myCircuitBreaker", URI.create("forward:/inCaseOfFailureUseThis/{segments}")))
.build();
}
}
----
@@ -126,6 +134,7 @@ However, you can also reroute the request to a controller or handler in an exter
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.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;
@@ -142,7 +151,8 @@ class RouteConfiguration {
.filter(circuitBreaker("fetchIngredients", URI.create("forward:/fallback")))
.build()
.and(route("ingredients-fallback")
.route(path("/fallback"), http("http://localhost:9994"))
.route(path("/fallback"), http())
.before(uri("https://localhost:9994"))
.build());
}
}

View File

@@ -14,6 +14,8 @@ spring:
routes:
- id: dedupe_response_header_route
uri: https://example.org
predicates:
- Path=/hello
filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
----
@@ -21,18 +23,19 @@ spring:
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.dedupeResponseHeader;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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<ServerResponse> gatewayRouterFunctionsDedupeResponseHeader() {
return route("dedupe_response_header_route")
.route(path("/hello"), http("https://example.org"))
.route(path("/hello"), http())
.before(uri("https://example.org"))
.after(dedupeResponseHeader("Access-Control-Allow-Credentials Access-Control-Allow-Origin"))
.build();
}

View File

@@ -14,7 +14,7 @@ spring:
- id: ingredients
uri: lb://ingredients
predicates:
- Path=//ingredients/**
- Path=/ingredients/**
filters:
- name: CircuitBreaker
args:
@@ -33,8 +33,9 @@ spring:
.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.BeforeFilterFunctions.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;
@@ -50,7 +51,8 @@ class RouteConfiguration {
.filter(circuitBreaker("fetchIngredients", URI.create("forward:/fallback")))
.build()
.and(route("ingredients-fallback")
.route(path("/fallback"), http("http://localhost:9994"))
.route(path("/fallback"), http())
.before(uri("http://localhost:9994"))
.before(fallbackHeaders())
.build());
}

View File

@@ -17,6 +17,8 @@ spring:
routes:
- id: map_request_header_route
uri: https://example.org
predicates:
- Path=/mypath
filters:
- MapRequestHeader=Blue, X-Request-Red
----
@@ -24,7 +26,8 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestParameter;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.mapRequestHeader;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@@ -34,7 +37,8 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsMapRequestHeader() {
return route("map_request_header_route")
.GET("/mypath", http("https://example.org"))
.GET("/mypath", http())
.before(uri("https://example.org"))
.before(mapRequestHeader("Blue", "X-Request-Red"))
.build();
}

View File

@@ -11,6 +11,7 @@ The following listing shows how to modify a request body filter:
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.modifyRequestBody;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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;
@@ -20,12 +21,13 @@ import org.springframework.http.MediaType;
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> 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();
public RouterFunction<ServerResponse> gatewayRouterFunctionsModifyRequestBody() {
return route("modify_request_body")
.route(host("*.modifyrequestbody.org"), http())
.before(uri("https://example.org"))
.before(modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,
(request, s) -> new Hello(s.toUpperCase())))
.build();
}
record Hello(String message) { }

View File

@@ -23,6 +23,7 @@ public RouterFunction<ServerResponse> gatewayRouterFunctionsModifyResponseBodySi
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.modifyResponseBody;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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;
@@ -32,11 +33,13 @@ import org.springframework.http.MediaType;
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> 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, response, s) -> s.toUpperCase()))
.build();
public RouterFunction<ServerResponse> gatewayRouterFunctionsModifyResponseBody() {
return route("modify_response_bodu")
.route(host("*.modifyresponsebodu.org"), http())
.before(uri("https://example.org"))
.after(modifyResponseBody(String.class, String.class, MediaType.APPLICATION_JSON_VALUE,
(request, s) -> s.toUpperCase()))
.build();
}
}

View File

@@ -15,6 +15,8 @@ spring:
routes:
- id: prefixpath_route
uri: https://example.org
predicates:
- Path=/**
filters:
- PrefixPath=/mypath
----
@@ -22,6 +24,7 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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;
@@ -31,10 +34,11 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsPrefixPath() {
return route("prefixpath_route")
.GET("/**", http("https://example.org"))
.before(prefixPath("/mypath"))
.build();
return route("prefixpath_route")
.GET("/**", http())
.before(uri("https://example.org"))
.before(prefixPath("/mypath"))
.build();
}
}
----

View File

@@ -16,6 +16,8 @@ spring:
routes:
- id: preserve_host_route
uri: https://example.org
predicates:
- Path=/**
filters:
- PreserveHostHeader
----
@@ -23,6 +25,7 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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;
@@ -32,10 +35,11 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsPreserveHostHeader() {
return route("preserve_host_route")
.GET("/**", http("https://example.org"))
.before(preserveHostHeader())
.build();
return route("preserve_host_route")
.GET("/**", http())
.before(uri("https://example.org"))
.before(preserveHostHeader())
.build();
}
}
----

View File

@@ -47,6 +47,7 @@ The following is an example of configuring a route with rate limiting:
.RouteConfiguration.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.filter.Bucket4jFilterFunctions.rateLimit;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@@ -56,12 +57,13 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsRateLimited() {
return route("rate_limited_route")
.GET("/api/**", http("https://example.org"))
.filter(rateLimit(c -> c.setCapacity(100)
.setPeriod(Duration.ofMinutes(1))
.setKeyResolver(request -> request.servletRequest().getUserPrincipal().getName())))
.build();
return route("rate_limited_route")
.GET("/api/**", http())
.before(uri("https://example.org"))
.filter(rateLimit(c -> c.setCapacity(100)
.setPeriod(Duration.ofMinutes(1))
.setKeyResolver(request -> request.servletRequest().getUserPrincipal().getName())))
.build();
}
}
----

View File

@@ -18,6 +18,8 @@ spring:
routes:
- id: redirectto_route
uri: https://example.org
predicates:
- Path=/**
filters:
- RedirectTo=302, https://acme.org
----
@@ -25,6 +27,7 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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;
@@ -34,10 +37,11 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsRedirectTo() {
return route("redirectto_route")
.GET("/**", http("https://example.org"))
.filter(redirectTo(302, URI.create("acme.org")))
.build();
return route("redirectto_route")
.GET("/**", http())
.before(uri("https://example.org"))
.filter(redirectTo(302, URI.create("acme.org")))
.build();
}
}
----

View File

@@ -16,6 +16,8 @@ spring:
routes:
- id: removerequestheader_route
uri: https://example.org
predicates:
- Path=/**
filters:
- RemoveRequestHeader=X-Request-Foo
----
@@ -23,6 +25,7 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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;
@@ -31,11 +34,12 @@ import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFuncti
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsremoveRequestHeader() {
return route("removerequestheader_route")
.GET("/**", http("https://example.org"))
.before(removeRequestHeader("X-Request-Foo"))
.build();
public RouterFunction<ServerResponse> gatewayRouterFunctionsRemoveRequestHeader() {
return route("removerequestheader_route")
.GET("/**", http())
.before(uri("https://example.org"))
.before(removeRequestHeader("X-Request-Foo"))
.build();
}
}
----

View File

@@ -16,6 +16,8 @@ spring:
routes:
- id: removerequestparameter_route
uri: https://example.org
predicates:
- Path=/**
filters:
- RemoveRequestParameter=red
----
@@ -23,7 +25,8 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestParameter;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.removeRequestParameter;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@@ -31,11 +34,12 @@ import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFuncti
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsAddReqHeader() {
return route("removerequestparameter_route")
.GET("/**", http("https://example.org"))
.before(removeRequestParameter("red"))
.build();
public RouterFunction<ServerResponse> gatewayRouterFunctionsRemoveRequestParameter() {
return route("removerequestparameter_route")
.GET("/**", http())
.before(uri("https://example.org"))
.before(removeRequestParameter("red"))
.build();
}
}
----

View File

@@ -15,6 +15,8 @@ spring:
routes:
- id: removeresponseheader_route
uri: https://example.org
predicates:
- Path=/anything/removeresponseheader
filters:
- RemoveResponseHeader=X-Response-Foo
----
@@ -22,6 +24,7 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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;
@@ -31,9 +34,10 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsRemoveResponseHeader() {
return route("addresponseheader")
.GET("/anything/addresheader", http("https://example.org"))
.after(removeResponseHeader("X-Response-Foo"))
return route("removeresponseheader_route")
.GET("/anything/removeresponseheader", http())
.before(uri("https://example.org"))
.after(removeResponseHeader("X-Response-Foo"))
.build();
}
}

View File

@@ -16,6 +16,8 @@ spring:
routes:
- id: requestheadersize_route
uri: https://example.org
predicates:
- Path=/**
filters:
- RequestHeaderSize=1000B
----
@@ -23,6 +25,7 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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;
@@ -32,10 +35,11 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsRequestHeaderSize() {
return route("requestheadersize_route")
.GET("/**", http("https://example.org"))
.before(requestHeaderSize("1000B"))
.build();
return route("requestheadersize_route")
.GET("/**", http())
.before(uri("https://example.org"))
.before(requestHeaderSize("1000B"))
.build();
}
}
----

View File

@@ -28,6 +28,7 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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;
@@ -37,10 +38,11 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsRequestSize() {
return route("request_size_route")
.GET("/upload", http("http://localhost:8080"))
.before(requestSize("5000000"))
.build();
return route("request_size_route")
.GET("/upload", http())
.before(uri("http://localhost:8080"))
.before(requestSize("5000000"))
.build();
}
}
----

View File

@@ -36,7 +36,7 @@ spring:
gateway:
mvc:
routes:
- id: retry_test
- id: retry_route
uri: http://localhost:8080/flakey
predicates:
- Host=*.retry.com
@@ -53,6 +53,7 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions.adaptCachedBody;
import static org.springframework.cloud.gateway.server.mvc.filter.RetryFilterFunctions.retry;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
@@ -63,12 +64,16 @@ import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequ
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsAddReqHeader() {
return route("add_request_parameter_route")
.route(host("*.retry.com"), http("https://example.org"))
.filter(retry(config -> config.setRetries(3).setSeries(Set.of(HttpStatus.Series.SERVER_ERROR)).setMethods(Set.of(HttpMethod.GET, HttpMethod.POST)).setCacheBody(true)))
.filter(adaptCachedBody())
.build();
public RouterFunction<ServerResponse> gatewayRouterFunctionsRetry() {
return route("retry_route")
.route(host("*.retry.com"), http())
.before(uri("http://localhost:8080/flakey"))
.filter(retry(config -> config.setRetries(3)
.setSeries(Set.of(HttpStatus.Series.SERVER_ERROR))
.setMethods(Set.of(HttpMethod.GET, HttpMethod.POST))
.setCacheBody(true)))
.filter(adaptCachedBody())
.build();
}
}
----
@@ -79,11 +84,11 @@ Instead, it should throw an `Exception` or signal an error (for example, through
NOTE: When using the retry filter, it will retry all filters that come after it. Make sure the results of the filters following the retry filter are as expected when they are executed multiple times.
// 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`.
WARNING: When using the retry filter with any HTTP method with a body and `cacheBody=true`, the body will be cached and the gateway will become memory constrained. The body is cached in a request attribute defined by `MvcUtils.CACHED_REQUEST_BODY_ATT`. The type of the object is `ByteArrayInputStream`.
A simplified "shortcut" notation can be added with a single `status` and `method`.
The following two examples are equivalent:
The following two example routes are equivalent:
.application.yml
[source,yaml]

View File

@@ -15,6 +15,8 @@ spring:
routes:
- id: rewritelocationresponseheader_route
uri: http://example.org
predicates:
- Path=/**
filters:
- RewriteLocationResponseHeader=AS_IN_REQUEST, Location, ,
----
@@ -22,7 +24,8 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.addResponseHeader;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.filter.RewriteLocationResponseHeaderFilterFunctions.rewriteLocationResponseHeader;
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;
@@ -33,9 +36,11 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsRewriteLocationResponseHeader() {
return route("rewritelocationresponseheader_route")
.GET("/**", http("https://example.org"))
.after(rewriteLocationResponseHeader(config -> config.setLocationHeaderName("Location").setStripVersion(StripVersion.AS_IN_REQUEST)))
.build();
.GET("/**", http())
.before(uri("https://example.org"))
.after(rewriteLocationResponseHeader(config -> config.setLocationHeaderName("Location")
.setStripVersion(StripVersion.AS_IN_REQUEST)))
.build();
}
}
----

View File

@@ -25,6 +25,7 @@ spring:
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.rewritePath;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@@ -33,10 +34,11 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsRewritePath() {
return route("rewritepath_route")
.GET("/red/**", http("https://example.org"))
.before(rewritePath("/red/(?<segment>.*)", "/${segment}"))
.build();
return route("rewritepath_route")
.GET("/red/**", http())
.before(uri("https://example.org"))
.before(rewritePath("/red/(?<segment>.*)", "/${segment}"))
.build();
}
}
----

View File

@@ -26,6 +26,7 @@ spring:
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestParameter;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@@ -33,11 +34,12 @@ import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFuncti
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsAddReqHeader() {
return route("add_request_parameter_route")
.GET("/anything/addrequestparam", http("https://example.org"))
.before(addRequestParameter("red", "blue"))
.build();
public RouterFunction<ServerResponse> gatewayRouterFunctionsRewriteRequestParameter() {
return route("rewriterequestparameter_route")
.GET("products", http())
.before(uri("https://example.org"))
.before(addRequestParameter("red", "blue"))
.build();
}
}
----

View File

@@ -16,14 +16,17 @@ spring:
routes:
- id: rewriteresponseheader_route
uri: https://example.org
predicates:
- Path=/**
filters:
- RewriteResponseHeader=X-Response-Red, , password=[^&]+, password=***
- 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.filter.AfterFilterFunctions.rewriteResponseHeader;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@@ -33,8 +36,9 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsRewriteResponseHeader() {
return route("rewriteresponseheader_route")
.GET("/**", http("https://example.org"))
.after(rewriteResponseHeader("X-Request-Red", "password=[^&]+", "password=***"))
.GET("/**", http())
.before(uri("https://example.org"))
.after(rewriteResponseHeader("X-Request-Red", "password=[^&]+", "password=***"))
.build();
}
}

View File

@@ -27,6 +27,7 @@ spring:
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.setPath;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@@ -35,10 +36,11 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsSetPath() {
return route("add_request_parameter_route")
.GET("/red/{segment}", http("https://example.org"))
.before(setPath("/{segment"))
.build();
return route("setpath_route")
.GET("/red/{segment}", http())
.before(uri("https://example.org"))
.before(setPath("/{segment"))
.build();
}
}
----

View File

@@ -14,6 +14,8 @@ spring:
routes:
- id: setrequestheader_route
uri: https://example.org
predicates:
- Path=/**
filters:
- SetRequestHeader=X-Request-Red, Blue
----
@@ -22,6 +24,7 @@ spring:
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.setRequestHostHeader;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@@ -30,10 +33,11 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsSetRequestHeader() {
return route("add_request_parameter_route")
.GET("/**", http("https://example.org"))
.before(setRequestHostHeader("X-Request-Red", "Blue"))
.build();
return route("setrequestheader_route")
.GET("/**", http())
.before(uri("https://example.org"))
.before(setRequestHostHeader("X-Request-Red", "Blue"))
.build();
}
}
----
@@ -64,19 +68,21 @@ spring:
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.setRequestHostHeader;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.host;
@Configuration
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsSetRequestHeader() {
return route("add_request_parameter_route")
.route(host("{segment}.myhost.org"), http("https://example.org"))
.before(setRequestHostHeader("X-Request-Red", "Blue-{segment}"))
.build();
return route("setrequestheader_route")
.route(host("{segment}.myhost.org"), http())
.before(uri("https://example.org"))
.before(setRequestHostHeader("X-Request-Red", "Blue-{segment}"))
.build();
}
}
----

View File

@@ -26,6 +26,7 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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;
@@ -35,10 +36,11 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsSetRequestHostHeader() {
return route("set_request_host_header_route")
.GET("/headers", http("http://localhost:8080"))
.before(setRequestHostHeader("example.org"))
.build();
return route("set_request_host_header_route")
.GET("/headers", http())
.before(uri("http://localhost:8080"))
.before(setRequestHostHeader("example.org"))
.build();
}
}
----

View File

@@ -14,6 +14,8 @@ spring:
routes:
- id: setresponseheader_route
uri: https://example.org
predicates:
- Path=/anything/setresponseheader
filters:
- SetResponseHeader=X-Response-Red, Blue
----
@@ -22,6 +24,7 @@ spring:
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.setResponseHeader;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@@ -30,9 +33,10 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsSetResponseHeader() {
return route("addresponseheader")
.GET("/anything/addresheader", http("https://example.org"))
.after(setResponseHeader("X-Response-Red", "Blue"))
return route("setresponseheader_route")
.GET("/anything/setresponseheader", http())
.before(uri("https://example.org"))
.after(setResponseHeader("X-Response-Red", "Blue"))
.build();
}
}
@@ -64,6 +68,7 @@ spring:
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.setResponseHeader;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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;
@@ -73,10 +78,11 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsSetResponseHeader() {
return route("add_response_header_route")
.route(host("{segment}.myhost.org"), http("https://example.org"))
.after(setResponseHeader("foo", "bar-{segment}"))
.build();
return route("setresponseheader_route")
.route(host("{segment}.myhost.org"), http())
.before(uri("https://example.org"))
.after(setResponseHeader("foo", "bar-{segment}"))
.build();
}
}
----

View File

@@ -16,10 +16,14 @@ spring:
routes:
- id: setstatusstring_route
uri: https://example.org
predicates:
- Path=/path1
filters:
- SetStatus=UNAUTHORIZED
- id: setstatusint_route
uri: https://example.org
predicates:
- Path=/path2
filters:
- SetStatus=401
----
@@ -28,6 +32,7 @@ spring:
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.setStatus;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@@ -37,13 +42,15 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsSetStatus() {
return route("setstatus_route")
.GET("/path1", http("https://example.org"))
.GET("/path1", http())
.before(uri("https://example.org"))
// setStatus("UNAUTHORIZED") works as well
.after(setStatus(HttpStatus.UNAUTHORIZED))
.build().and(route("setstatusint_route")
.GET("/path2", http("https://example.org"))
.before(uri("https://example.org"))
.GET("/path2", http())
.after(setStatus("401"))
.build());
.build());
}
}
----

View File

@@ -13,8 +13,8 @@ spring:
gateway:
mvc:
routes:
- id: nameRoot
uri: https://nameservice
- id: strip_prefix_route
uri: https://example.org
predicates:
- Path=/name/**
filters:
@@ -25,6 +25,7 @@ spring:
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.stripPrefix;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@@ -33,14 +34,15 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsStripPrefix() {
return route("nameRoot")
.GET("/name/**", http("https://example.org"))
.before(stripPrefix(2))
.build();
return route("strip_prefix_route")
.GET("/name/**", http())
.before(uri("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`.
When a request is made through the gateway to `/name/blue/red`, the full request url looks like `https://example.org/red`.
WARNING: If using the `lb()` filter, it needs to be after the `stripPrefix()` filter, otherwise the resulting url could be incorrect. The `lb:` scheme handler in configuration, automatically puts the filter in the highest precedence order.

View File

@@ -54,6 +54,7 @@ Spring Cloud Gateway Server MVC can forward the OAuth2 access token of the curre
.RouteConfiguration.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.filter.TokenRelayFilterFunctions.tokenRelay;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@@ -62,11 +63,12 @@ import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFuncti
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsAddReqHeader() {
return route("resource")
.GET("/resource", http("http://localhost:9000"))
.filter(tokenRelay())
.build();
public RouterFunction<ServerResponse> gatewayRouterFunctionsTokenRelay() {
return route("resource")
.GET("/resource", http())
.before(uri("https://localhost:9000"))
.filter(tokenRelay())
.build();
}
}
----

View File

@@ -31,6 +31,7 @@ spring:
[source,java]
----
import java.time.ZonedDateTime;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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.after;
@@ -40,9 +41,10 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsAfter() {
return route("after_route")
.route(after(ZonedDateTime.parse("2017-01-20T17:42:47.789-07:00[America/Denver]")), http("https://example.org"))
.build();
return route("after_route")
.route(after(ZonedDateTime.parse("2017-01-20T17:42:47.789-07:00[America/Denver]")), http())
.before(uri("https://example.org"))
.build();
}
}
----
@@ -74,6 +76,7 @@ spring:
[source,java]
----
import java.time.ZonedDateTime;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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.before;
@@ -83,9 +86,10 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsBefore() {
return route("before_route")
.route(before(ZonedDateTime.parse("2017-01-20T17:42:47.789-07:00[America/Denver]")), http("https://example.org"))
.build();
return route("before_route")
.route(before(ZonedDateTime.parse("2017-01-20T17:42:47.789-07:00[America/Denver]")), http()
.before(uri("https://example.org"))
.build();
}
}
----
@@ -119,6 +123,7 @@ spring:
[source,java]
----
import java.time.ZonedDateTime;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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.between;
@@ -128,9 +133,11 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsBetween() {
return route("between_route")
.route(between(ZonedDateTime.parse("2017-01-20T17:42:47.789-07:00[America/Denver]"), ZonedDateTime.parse("2017-01-21T17:42:47.789-07:00[America/Denver]")), http("https://example.org"))
.build();
return route("between_route")
.route(between(ZonedDateTime.parse("2017-01-20T17:42:47.789-07:00[America/Denver]"),
ZonedDateTime.parse("2017-01-21T17:42:47.789-07:00[America/Denver]")), http())
.before(uri("https://example.org"))
.build();
}
}
----
@@ -162,18 +169,20 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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.between;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.cookie;
@Configuration
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsCookie() {
return route("cookie_route")
.route(cookie("chocolate", "ch.p"), http("https://example.org"))
.build();
return route("cookie_route")
.route(cookie("chocolate", "ch.p"), http())
.before(uri("https://example.org"))
.build();
}
}
----
@@ -204,6 +213,7 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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.header;
@@ -212,10 +222,11 @@ import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequ
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsCookie() {
return route("cookie_route")
.route(header("X-Request-Id", "\\d+"), http("https://example.org"))
.build();
public RouterFunction<ServerResponse> gatewayRouterFunctionsHeader() {
return route("header_route")
.route(header("X-Request-Id", "\\d+"), http())
.before(uri("https://example.org"))
.build();
}
}
----
@@ -247,6 +258,7 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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;
@@ -256,9 +268,10 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsHost() {
return route("host_route")
.route(host("**.somehost.org", "**.anotherhost.org"), http("https://example.org"))
.build();
return route("host_route")
.route(host("**.somehost.org", "**.anotherhost.org"), http())
.before(uri("https://example.org"))
.build();
}
}
----
@@ -296,6 +309,7 @@ spring:
[source,java]
----
import org.springframework.http.HttpMethod;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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.method;
@@ -305,9 +319,10 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsMethod() {
return route("method_route")
.route(method(HttpMethod.GET, HttpMethod.POST), http("https://example.org"))
.build();
return route("method_route")
.route(method(HttpMethod.GET, HttpMethod.POST), http())
.before(uri("https://example.org"))
.build();
}
}
----
@@ -319,19 +334,19 @@ This route matches if the request method was a `GET` or a `POST`.
.GatewaySampleApplication.java
[source,java]
----
import org.springframework.http.HttpMethod;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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.methods;
@Configuration
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsMethod() {
return route("method_route")
.GET("/mypath", http("https://example.org"))
.build();
public RouterFunction<ServerResponse> gatewayRouterFunctionsMethodAndPath() {
return route("method_and_path_route")
.GET("/mypath", http())
.before(uri("https://example.org"))
.build();
}
}
----
@@ -363,19 +378,20 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import org.springframework.http.HttpMethod;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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.method;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.path;
@Configuration
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsPath() {
return route("path_route")
.route(path("/red/{segment}", "/blue/{segment}"), http("https://example.org"))
.build();
return route("path_route")
.route(path("/red/{segment}", "/blue/{segment}"), http())
.before(uri("https://example.org"))
.build();
}
}
----
@@ -421,7 +437,7 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import org.springframework.http.HttpMethod;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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.query;
@@ -431,9 +447,10 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsQuery() {
return route("query_route")
.route(query("green"), http("https://example.org"))
.build();
return route("query_route")
.route(query("green"), http())
.before(uri("https://example.org"))
.build();
}
}
----
@@ -457,7 +474,7 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import org.springframework.http.HttpMethod;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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.query;
@@ -467,9 +484,10 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsQuery() {
return route("query_route")
.route(query("red", "gree."), http("https://example.org"))
.build();
return route("query_route")
.route(query("red", "gree."), http())
.before(uri("https://example.org"))
.build();
}
}
----
@@ -585,22 +603,25 @@ spring:
.GatewaySampleApplication.java
[source,java]
----
import org.springframework.http.HttpMethod;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
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.method;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.path;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.weight;
@Configuration
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsWeights() {
return route("weight_high")
.route(weight("group1", 8).and(path("/**")), http("https://weighthigh.org"))
.build().and(
route("weight_low")
.route(weight("group1", 2).and(path("/**")), http("https://weightlow.org"))
.build());
return route("weight_high")
.route(weight("group1", 8).and(path("/**")), http())
.before(uri("https://weighthigh.org"))
.build().and(
route("weight_low")
.route(weight("group1", 2).and(path("/**")), http())
.before(uri("https://weightlow.org"))
.build());
}
}
----

View File

@@ -9,12 +9,15 @@ A https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springfra
[source,java]
----
import static org.springframework.web.servlet.function.RouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
class SimpleGateway {
@Bean
public RouterFunction<ServerResponse> getRoute() {
return route().GET("/get", http("https://httpbin.org")).build();
return route().GET("/get", http())
.before(uri("https://example.org"))
.build();
}
}
----
@@ -29,13 +32,16 @@ Some advanced filters require some metadata to be added to request attributes. T
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
class SimpleGateway {
@Bean
public RouterFunction<ServerResponse> getRoute() {
return route("simple_route").GET("/get", http("https://httpbin.org")).build();
return route("simple_route").GET("/get", http())
.before(uri("https://example.org"))
.build();
}
}
----
@@ -43,4 +49,6 @@ class SimpleGateway {
[[gateway-handlerfunctions]]
== Gateway MVC Handler Functions
Various `RouterFunctions.Builder` methods require a `HandlerFunction<ServerResponse>`. 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`.
Various `RouterFunctions.Builder` methods require a `HandlerFunction<ServerResponse>`. 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`. 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`.
WARNING: As of version 4.1.7, `HandlerFunctions.http(String)` and `HandlerFunctions.http(URI)` are now deprecated. Please use `HandlerFunctions.http()` in combination with the `BeforeFilterFunctions.uri()` filter instead. This fixes inconsistencies in dealing with the route url request attribute.

View File

@@ -48,6 +48,7 @@ To use our new `headerExists` `RequestPredicate`, we need to plug it in to an ap
[source,java]
----
import static SampleRequestPredicates.headerExists;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@@ -56,9 +57,10 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> headerExistsRoute() {
return route("header_exists_route")
.route(headerExists("X-Green"), http("https://example.org"))
.build();
return route("header_exists_route")
.route(headerExists("X-Green"), http())
.before(uri("https://example.org"))
.build();
}
}
----
@@ -111,6 +113,7 @@ First, a new `ServerRequest` is created from the existing request. This allows u
[source,java]
----
import static SampleHandlerFilterFunctions.instrument;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@@ -120,9 +123,10 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> instrumentRoute() {
return route("instrument_route")
.GET("/**", http("https://example.org"))
.filter(instrument("X-Request-Id", "X-Response-Id"))
.build();
.GET("/**", http())
.filter(instrument("X-Request-Id", "X-Response-Id"))
.before(uri("https://example.org"))
.build();
}
}
----
@@ -160,6 +164,7 @@ A new `ServerRequest` is created from the existing request. This allows us to ad
[source,java]
----
import static SampleBeforeFilterFunctions.instrument;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@@ -168,10 +173,10 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> instrumentRoute() {
return route("instrument_route")
.GET("/**", http("https://example.org"))
.before(instrument("X-Request-Id"))
.build();
return route("instrument_route").GET("/**", http())
.before(uri("https://example.org"))
.before(instrument("X-Request-Id"))
.build();
}
}
----
@@ -213,6 +218,7 @@ In this case we simply add the header to the response and return it.
[source,java]
----
import static SampleAfterFilterFunctions.instrument;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@@ -221,10 +227,11 @@ class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> instrumentRoute() {
return route("instrument_route")
.GET("/**", http("https://example.org"))
.after(instrument("X-Response-Id"))
.build();
return route("instrument_route")
.GET("/**", http())
.before(uri("https://example.org"))
.after(instrument("X-Response-Id"))
.build();
}
}
----

View File

@@ -39,7 +39,6 @@ import org.springframework.boot.context.properties.source.ConfigurationPropertyS
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
import org.springframework.cloud.gateway.server.mvc.common.Configurable;
import org.springframework.cloud.gateway.server.mvc.common.MvcUtils;
import org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions;
import org.springframework.cloud.gateway.server.mvc.filter.FilterDiscoverer;
import org.springframework.cloud.gateway.server.mvc.handler.HandlerDiscoverer;
import org.springframework.cloud.gateway.server.mvc.invoke.InvocationContext;
@@ -150,15 +149,6 @@ public class RouterFunctionHolderFactory {
RouterFunctions.Builder builder = route(routeId);
// MVC.fn users won't need this anonymous filter as url will be set directly.
// Put this function first, so if a filter from a handler changes the url
// it is after this one.
builder.filter((request, next) -> {
MvcUtils.setRequestUrl(request, routeProperties.getUri());
return next.handle(request);
});
builder.before(BeforeFilterFunctions.routeId(routeId));
MultiValueMap<String, OperationMethod> handlerOperations = handlerDiscoverer.getOperations();
// TODO: cache?
// translate handlerFunction
@@ -181,13 +171,15 @@ public class RouterFunctionHolderFactory {
HandlerFunction<ServerResponse> handlerFunction = null;
// filters added by HandlerDiscoverer need to go last, so save them
List<HandlerFilterFunction<ServerResponse, ServerResponse>> handlerFilterFunctionFilters = new ArrayList<>();
List<HandlerFilterFunction<ServerResponse, ServerResponse>> lowerPrecedenceFilters = new ArrayList<>();
List<HandlerFilterFunction<ServerResponse, ServerResponse>> higherPrecedenceFilters = new ArrayList<>();
if (response instanceof HandlerFunction<?>) {
handlerFunction = (HandlerFunction<ServerResponse>) response;
}
else if (response instanceof HandlerDiscoverer.Result result) {
handlerFunction = result.getHandlerFunction();
handlerFilterFunctionFilters.addAll(result.getFilters());
lowerPrecedenceFilters.addAll(result.getLowerPrecedenceFilters());
higherPrecedenceFilters.addAll(result.getHigherPrecedenceFilters());
}
if (handlerFunction == null) {
throw new IllegalStateException(
@@ -218,6 +210,9 @@ public class RouterFunctionHolderFactory {
builder.route(predicate.get(), handlerFunction);
predicate.set(null);
// HandlerDiscoverer filters needing lower priority, so put them first
lowerPrecedenceFilters.forEach(builder::filter);
// translate filters
MultiValueMap<String, OperationMethod> filterOperations = filterDiscoverer.getOperations();
routeProperties.getFilters().forEach(filterProperties -> {
@@ -226,7 +221,7 @@ public class RouterFunctionHolderFactory {
});
// HandlerDiscoverer filters need higher priority, so put them last
handlerFilterFunctionFilters.forEach(builder::filter);
higherPrecedenceFilters.forEach(builder::filter);
builder.withAttribute(MvcUtils.GATEWAY_ROUTE_ID_ATTR, routeId);

View File

@@ -196,7 +196,6 @@ public abstract class BeforeFilterFunctions {
String newPath = uri.getRawPath() + request.uri().getRawPath();
URI prefixedUri = UriComponentsBuilder.fromUri(request.uri()).replacePath(newPath).build().toUri();
MvcUtils.setRequestUrl(request, prefixedUri);
return ServerRequest.from(request).uri(prefixedUri).build();
};
}
@@ -340,7 +339,6 @@ public abstract class BeforeFilterFunctions {
ServerRequest modified = ServerRequest.from(request).uri(rewrittenUri).build();
MvcUtils.setRequestUrl(request, rewrittenUri);
return modified;
};
}
@@ -361,7 +359,6 @@ public abstract class BeforeFilterFunctions {
URI uri = uriTemplate.expand(uriVariables);
URI newUri = UriComponentsBuilder.fromUri(request.uri()).replacePath(uri.getRawPath()).build(true).toUri();
MvcUtils.setRequestUrl(request, newUri);
return ServerRequest.from(request).uri(newUri).build();
};
}
@@ -419,12 +416,22 @@ public abstract class BeforeFilterFunctions {
.replacePath(newPath.toString())
.build(true)
.toUri();
MvcUtils.setRequestUrl(request, prefixedUri);
return ServerRequest.from(request).uri(prefixedUri).build();
};
}
public static Function<ServerRequest, ServerRequest> uri(String uri) {
return uri(URI.create(uri));
}
public static Function<ServerRequest, ServerRequest> uri(URI uri) {
return request -> {
MvcUtils.setRequestUrl(request, uri);
return request;
};
}
public static class FallbackHeadersConfig {
private String executionExceptionTypeHeaderName = CB_EXECUTION_EXCEPTION_TYPE;

View File

@@ -228,6 +228,15 @@ public interface FilterFunctions {
return ofResponseProcessor(AfterFilterFunctions.setStatus(statusCode));
}
static HandlerFilterFunction<ServerResponse, ServerResponse> uri(String uri) {
return ofRequestProcessor(BeforeFilterFunctions.uri(uri));
}
@Shortcut
static HandlerFilterFunction<ServerResponse, ServerResponse> uri(URI uri) {
return ofRequestProcessor(BeforeFilterFunctions.uri(uri));
}
class FilterSupplier extends SimpleFilterSupplier {
public FilterSupplier() {

View File

@@ -40,7 +40,7 @@ public class LoadBalancerHandlerSupplier implements HandlerSupplier {
public static HandlerDiscoverer.Result lb(URI uri) {
// TODO: how to do something other than http
return new HandlerDiscoverer.Result(HandlerFunctions.http(),
return new HandlerDiscoverer.Result(HandlerFunctions.http(), Collections.emptyList(),
Collections.singletonList(LoadBalancerFilterFunctions.lb(uri.getHost())));
}

View File

@@ -400,7 +400,7 @@ public class XForwardedRequestHeadersFilter implements HttpHeadersFilter.Request
LinkedHashSet<URI> originalUris = MvcUtils.getAttribute(request,
MvcUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
URI requestUri = MvcUtils.getAttribute(request, MvcUtils.GATEWAY_REQUEST_URL_ATTR);
URI requestUri = request.uri();
if (originalUris != null && requestUri != null) {

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2013-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.gateway.server.mvc.handler;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import org.springframework.cloud.gateway.server.mvc.common.MvcUtils;
import org.springframework.cloud.gateway.server.mvc.config.RouteProperties;
import org.springframework.web.servlet.function.HandlerFilterFunction;
import org.springframework.web.servlet.function.HandlerFunction;
import org.springframework.web.servlet.function.ServerResponse;
class DefaultHandlerSupplier implements HandlerSupplier {
@Override
public Collection<Method> get() {
return Arrays.asList(getClass().getMethods());
}
public static HandlerDiscoverer.Result forward(RouteProperties routeProperties) {
return forward(routeProperties.getId(), routeProperties.getUri());
}
public static HandlerDiscoverer.Result forward(String id, URI uri) {
return new HandlerDiscoverer.Result(HandlerFunctions.forward(uri.getPath()), Collections.emptyList());
}
public static HandlerDiscoverer.Result http(RouteProperties routeProperties) {
return http(routeProperties.getId(), routeProperties.getUri());
}
public static HandlerDiscoverer.Result http(String id, URI uri) {
HandlerFunction<ServerResponse> http = HandlerFunctions.http();
return getResult(id, uri, http);
}
public static HandlerDiscoverer.Result https(RouteProperties routeProperties) {
return https(routeProperties.getId(), routeProperties.getUri());
}
public static HandlerDiscoverer.Result https(String id, URI uri) {
return getResult(id, uri, HandlerFunctions.https());
}
public static HandlerDiscoverer.Result no(RouteProperties routeProperties) {
return no(routeProperties.getId(), routeProperties.getUri());
}
public static HandlerDiscoverer.Result no(String id, URI uri) {
return getResult(id, uri, HandlerFunctions.no());
}
private static HandlerDiscoverer.Result getResult(String id, URI uri,
HandlerFunction<ServerResponse> handlerFunction) {
HandlerFilterFunction<ServerResponse, ServerResponse> setId = setIdFilter(id);
HandlerFilterFunction<ServerResponse, ServerResponse> setRequest = setRequestUrlFilter(uri);
return new HandlerDiscoverer.Result(handlerFunction, Arrays.asList(setId, setRequest), Collections.emptyList());
}
private static HandlerFilterFunction<ServerResponse, ServerResponse> setIdFilter(String id) {
return (request, next) -> {
MvcUtils.setRouteId(request, id);
return next.handle(request);
};
}
private static HandlerFilterFunction<ServerResponse, ServerResponse> setRequestUrlFilter(URI uri) {
return (request, next) -> {
MvcUtils.setRequestUrl(request, uri);
return next.handle(request);
};
}
}

View File

@@ -37,20 +37,39 @@ public class HandlerDiscoverer extends AbstractGatewayDiscoverer {
private final HandlerFunction<ServerResponse> handlerFunction;
private final List<HandlerFilterFunction<ServerResponse, ServerResponse>> filters;
private final List<HandlerFilterFunction<ServerResponse, ServerResponse>> lowerPrecedenceFilters;
private final List<HandlerFilterFunction<ServerResponse, ServerResponse>> higherPrecedenceFilters;
@Deprecated
public Result(HandlerFunction<ServerResponse> handlerFunction,
List<HandlerFilterFunction<ServerResponse, ServerResponse>> filters) {
this(handlerFunction, Collections.emptyList(), filters);
}
public Result(HandlerFunction<ServerResponse> handlerFunction,
List<HandlerFilterFunction<ServerResponse, ServerResponse>> lowerPrecedenceFilters,
List<HandlerFilterFunction<ServerResponse, ServerResponse>> higherPrecedenceFilters) {
this.handlerFunction = handlerFunction;
this.filters = Objects.requireNonNullElse(filters, Collections.emptyList());
this.lowerPrecedenceFilters = Objects.requireNonNullElse(lowerPrecedenceFilters, Collections.emptyList());
this.higherPrecedenceFilters = Objects.requireNonNullElse(higherPrecedenceFilters, Collections.emptyList());
}
public HandlerFunction<ServerResponse> getHandlerFunction() {
return handlerFunction;
}
@Deprecated
public List<HandlerFilterFunction<ServerResponse, ServerResponse>> getFilters() {
return filters;
return getHigherPrecedenceFilters();
}
public List<HandlerFilterFunction<ServerResponse, ServerResponse>> getLowerPrecedenceFilters() {
return lowerPrecedenceFilters;
}
public List<HandlerFilterFunction<ServerResponse, ServerResponse>> getHigherPrecedenceFilters() {
return higherPrecedenceFilters;
}
}

View File

@@ -19,8 +19,8 @@ package org.springframework.cloud.gateway.server.mvc.handler;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicReference;
import jakarta.servlet.ServletException;
@@ -60,14 +60,17 @@ public abstract class HandlerFunctions {
// TODO: current discovery only goes by method name
// so last one wins, so put parameterless last
@Deprecated
public static HandlerFunction<ServerResponse> http(String uri) {
return http(URI.create(uri));
}
@Deprecated
public static HandlerFunction<ServerResponse> http(URI uri) {
return new LookupProxyExchangeHandlerFunction(uri);
}
@Deprecated
public static HandlerFunction<ServerResponse> https(URI uri) {
return new LookupProxyExchangeHandlerFunction(uri);
}
@@ -86,6 +89,7 @@ public abstract class HandlerFunctions {
static class LookupProxyExchangeHandlerFunction implements HandlerFunction<ServerResponse> {
@Deprecated
private final URI uri;
private AtomicReference<ProxyExchangeHandlerFunction> proxyExchangeHandlerFunction = new AtomicReference<>();
@@ -94,6 +98,7 @@ public abstract class HandlerFunctions {
this.uri = null;
}
@Deprecated
LookupProxyExchangeHandlerFunction(URI uri) {
this.uri = uri;
}
@@ -101,12 +106,15 @@ public abstract class HandlerFunctions {
@Override
public ServerResponse handle(ServerRequest serverRequest) {
if (uri != null) {
// TODO: in 2 places now, here and
// GatewayMvcPropertiesBeanDefinitionRegistrar
MvcUtils.putAttribute(serverRequest, MvcUtils.GATEWAY_REQUEST_URL_ATTR, uri);
// TODO: log warning of deprecated usage
MvcUtils.setRequestUrl(serverRequest, uri);
}
this.proxyExchangeHandlerFunction.compareAndSet(null, lookup(serverRequest));
return proxyExchangeHandlerFunction.get().handle(serverRequest);
return proxyExchangeHandlerFunction.updateAndGet(function -> {
if (function == null) {
return lookup(serverRequest);
}
return function;
}).handle(serverRequest);
}
private static ProxyExchangeHandlerFunction lookup(ServerRequest request) {
@@ -124,12 +132,13 @@ public abstract class HandlerFunctions {
}
@Deprecated
public static class HandlerSupplier
implements org.springframework.cloud.gateway.server.mvc.handler.HandlerSupplier {
@Override
public Collection<Method> get() {
return Arrays.asList(HandlerFunctions.class.getMethods());
return Collections.emptyList();
}
}

View File

@@ -23,7 +23,7 @@ org.springframework.cloud.gateway.server.mvc.filter.FilterSupplier=\
org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions.FilterSupplier
org.springframework.cloud.gateway.server.mvc.handler.HandlerSupplier=\
org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.HandlerSupplier,\
org.springframework.cloud.gateway.server.mvc.handler.DefaultHandlerSupplier,\
org.springframework.cloud.gateway.server.mvc.filter.LoadBalancerHandlerSupplier
org.springframework.cloud.gateway.server.mvc.predicate.PredicateSupplier=\

View File

@@ -76,6 +76,7 @@ import org.springframework.test.context.ContextConfiguration;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;
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.RestController;
@@ -107,6 +108,7 @@ import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFu
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.requestHeaderToRequestUri;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.requestSize;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.routeId;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.filter.Bucket4jFilterFunctions.rateLimit;
import static org.springframework.cloud.gateway.server.mvc.filter.CircuitBreakerFilterFunctions.circuitBreaker;
import static org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions.addRequestHeader;
@@ -298,7 +300,7 @@ public class ServerMvcIntegrationTests {
XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER);
assertThat(headers).containsEntry(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
"/long/path/to");
assertThat(headers).containsEntry("X-Test", "stripPrefix");
assertThat(headers).containsEntry("X-Test", "stripPrefixLb");
});
}
@@ -418,7 +420,8 @@ public class ServerMvcIntegrationTests {
public void circuitBreakerInvalidFallbackThrowsException() {
// @formatter:off
Assertions.assertThatThrownBy(() -> route("testcircuitbreakergatewayfallback")
.route(path("/anything/circuitbreakergatewayfallback"), http(URI.create("https://nonexistantdomain.com1234")))
.route(path("/anything/circuitbreakergatewayfallback"), http())
.before(uri("https://nonexistantdomain.com1234"))
.filter(circuitBreaker("mycb2", URI.create("http://example.com")))
.build()).isInstanceOf(IllegalArgumentException.class);
// @formatter:on
@@ -1016,6 +1019,11 @@ public class ServerMvcIntegrationTests {
@LoadBalancerClient(name = "httpbin", configuration = TestLoadBalancerConfig.Httpbin.class)
protected static class TestConfiguration {
@Bean
StaticPortController staticPortController() {
return new StaticPortController();
}
@Bean
TestHandler testHandler() {
return new TestHandler();
@@ -1132,7 +1140,7 @@ public class ServerMvcIntegrationTests {
return route("teststripprefix")
.route(GET("/long/path/to/get").and(host("**.stripprefixlb.org")), http())
.filter(stripPrefix(3))
.filter(addRequestHeader("X-Test", "stripPrefix"))
.filter(addRequestHeader("X-Test", "stripPrefixLb"))
.filter(lb("httpbin"))
.build();
// @formatter:on
@@ -1185,7 +1193,8 @@ public class ServerMvcIntegrationTests {
public RouterFunction<ServerResponse> gatewayRouterFunctionsCircuitBreakerFallback() {
// @formatter:off
return route("testcircuitbreakerfallback")
.route(path("/anything/circuitbreakerfallback"), http(URI.create("https://nonexistantdomain.com1234")))
.route(path("/anything/circuitbreakerfallback"), http())
.before(uri("https://nonexistantdomain.com1234"))
.filter(circuitBreaker("mycb1", "/hello"))
.build();
// @formatter:on
@@ -1195,7 +1204,8 @@ public class ServerMvcIntegrationTests {
public RouterFunction<ServerResponse> gatewayRouterFunctionsCircuitBreakerFallbackToGatewayRoute() {
// @formatter:off
return route("testcircuitbreakergatewayfallback")
.route(path("/anything/circuitbreakergatewayfallback"), http(URI.create("https://nonexistantdomain.com1234")))
.route(path("/anything/circuitbreakergatewayfallback"), http())
.before(uri("https://nonexistantdomain.com1234"))
.filter(circuitBreaker("mycb2", URI.create("forward:/anything/gatewayfallback")))
.build()
.and(route("testgatewayfallback")
@@ -1686,6 +1696,16 @@ public class ServerMvcIntegrationTests {
}
@RestController
protected static class StaticPortController {
@GetMapping(path = "/anything/staticport", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> messageEvents() {
return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).build();
}
}
@RestController
protected static class EventController {

View File

@@ -156,6 +156,18 @@ class BeforeFilterFunctionsTests {
assertThat(result.uri().toString()).hasToString("http://localhost/depth3");
}
@Test
void stripPrefixWithPort() {
MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost:77/depth1/depth2/depth3")
.buildRequest(null);
ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList());
ServerRequest result = BeforeFilterFunctions.stripPrefix(2).apply(request);
assertThat(result.uri().toString()).hasToString("http://localhost:77/depth3");
}
@Test
void stripPrefixWithEncodedPath() {
MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/depth1/depth2/depth3/é")

View File

@@ -0,0 +1,141 @@
/*
* Copyright 2013-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.gateway.server.mvc.filter;
import java.util.Map;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.gateway.server.mvc.test.HttpbinTestcontainers;
import org.springframework.cloud.gateway.server.mvc.test.TestLoadBalancerConfig;
import org.springframework.cloud.gateway.server.mvc.test.client.TestRestClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions.stripPrefix;
import static org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions.uri;
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.test.TestUtils.getMap;
@SuppressWarnings("unchecked")
@SpringBootTest(properties = {}, webEnvironment = WebEnvironment.RANDOM_PORT)
@ActiveProfiles("stripprefixstaticport")
@ContextConfiguration(initializers = HttpbinTestcontainers.class)
public class StripPrefixStaticPortTests {
@Autowired
TestRestClient restClient;
@BeforeAll
static void beforeAll() {
HttpbinTestcontainers.initializeSystemProperties();
}
@Test
public void stripPrefixStaticPort() {
restClient.get()
.uri("/long/path/to/anything/staticport")
.exchange()
.expectStatus()
.isOk()
.expectBody(Map.class)
.consumeWith(res -> {
Map<String, Object> map = res.getResponseBody();
Map<String, Object> headers = getMap(map, "headers");
assertThat(headers)
.containsKeys(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER,
XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER,
XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER,
XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER)
.containsEntry(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, "/long/path/to");
});
}
@Test
public void stripPrefixStaticPortDsl() {
restClient.get()
.uri("/long/path/to/anything/staticportdsl")
.exchange()
.expectStatus()
.isOk()
.expectBody(Map.class)
.consumeWith(res -> {
Map<String, Object> map = res.getResponseBody();
Map<String, Object> headers = getMap(map, "headers");
assertThat(headers)
.containsKeys(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER,
XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER,
XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER,
XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER)
.containsEntry(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, "/long/path/to");
});
}
@SpringBootConfiguration
@EnableAutoConfiguration
@LoadBalancerClient(name = "httpbin", configuration = TestLoadBalancerConfig.Httpbin.class)
protected static class TestConfiguration {
@Bean
StaticPortController staticPortController() {
return new StaticPortController();
}
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsStripPrefixStaticPortDsl(Environment env) {
// @formatter:off
return route("teststripprefixstaticportdsl")
.GET("/long/path/to/anything/staticportdsl", http())
.filter(uri(env.getProperty("strip.prefix.static.uri")))
.filter(stripPrefix(3))
.build();
// @formatter:on
}
}
@RestController
protected static class StaticPortController {
@GetMapping(path = "/anything/staticport", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> messageEvents() {
return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).build();
}
}
}

View File

@@ -18,8 +18,6 @@ package org.springframework.cloud.gateway.server.mvc.test;
import java.util.HashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
import org.testcontainers.utility.DockerImageName;
@@ -35,8 +33,6 @@ public class HttpbinTestcontainers implements ApplicationContextInitializer<Conf
// https://hub.docker.com/r/mccutchen/go-httpbin
private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("mccutchen/go-httpbin");
static final Logger logger = LoggerFactory.getLogger(HttpbinTestcontainers.class);
/**
* Default httpbin port.
*/
@@ -59,7 +55,6 @@ public class HttpbinTestcontainers implements ApplicationContextInitializer<Conf
MutablePropertySources sources = context.getEnvironment().getPropertySources();
if (!sources.contains("httpbinTestcontainer")) {
boolean running = container.isRunning();
Integer mappedPort = container.getMappedPort(DEFAULT_PORT);
HashMap<String, Object> map = new HashMap<>();
map.put("httpbin.port", String.valueOf(mappedPort));

View File

@@ -0,0 +1,12 @@
strip.prefix.static.uri: http://${httpbin.host}:${httpbin.port}
spring.cloud.gateway.mvc:
routes:
- id: strip_prefix_static_port_config
uri: ${strip.prefix.static.uri}
predicates:
- Path=/long/path/to/anything/staticport
filters:
- StripPrefix=3
logging:
level:
org.springframework.cloud.gateway.server.mvc: TRACE