Merge branch '2.1.x'
# Conflicts: # spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpointTests.java
This commit is contained in:
@@ -847,7 +847,7 @@ spring:
|
||||
predicates:
|
||||
- Path=/foo/**
|
||||
filters:
|
||||
- RewritePath=/foo/(?<segment>.*), /$\{segment}
|
||||
- RewritePath=/foo(?<segment>/?.*), $\{segment}
|
||||
----
|
||||
|
||||
For a request path of `/foo/bar`, this will set the path to `/bar` before making the downstream request. Notice the `$` Should be replaced with `$\` because of the YAML spec.
|
||||
|
||||
@@ -28,6 +28,7 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
|
||||
@@ -56,6 +57,8 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
|
||||
protected List<GatewayFilterFactory> GatewayFilters;
|
||||
|
||||
protected List<RoutePredicateFactory> routePredicates;
|
||||
|
||||
protected RouteDefinitionWriter routeDefinitionWriter;
|
||||
|
||||
protected RouteLocator routeLocator;
|
||||
@@ -65,10 +68,12 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
public AbstractGatewayControllerEndpoint(
|
||||
RouteDefinitionLocator routeDefinitionLocator,
|
||||
List<GlobalFilter> globalFilters, List<GatewayFilterFactory> GatewayFilters,
|
||||
List<RoutePredicateFactory> routePredicates,
|
||||
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
|
||||
this.routeDefinitionLocator = routeDefinitionLocator;
|
||||
this.globalFilters = globalFilters;
|
||||
this.GatewayFilters = GatewayFilters;
|
||||
this.routePredicates = routePredicates;
|
||||
this.routeDefinitionWriter = routeDefinitionWriter;
|
||||
this.routeLocator = routeLocator;
|
||||
}
|
||||
@@ -96,6 +101,11 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
return getNamesToOrders(this.GatewayFilters);
|
||||
}
|
||||
|
||||
@GetMapping("/routepredicates")
|
||||
public Mono<HashMap<String, Object>> routepredicates() {
|
||||
return getNamesToOrders(this.routePredicates);
|
||||
}
|
||||
|
||||
private <T> Mono<HashMap<String, Object>> getNamesToOrders(List<T> list) {
|
||||
return Flux.fromIterable(list).reduce(new HashMap<>(), this::putItem);
|
||||
}
|
||||
@@ -117,14 +127,35 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
*/
|
||||
@PostMapping("/routes/{id}")
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<ResponseEntity<Void>> save(@PathVariable String id,
|
||||
@RequestBody Mono<RouteDefinition> route) {
|
||||
return this.routeDefinitionWriter.save(route.map(r -> {
|
||||
r.setId(id);
|
||||
log.debug("Saving route: " + route);
|
||||
return r;
|
||||
})).then(Mono.defer(() -> Mono
|
||||
.just(ResponseEntity.created(URI.create("/routes/" + id)).build())));
|
||||
public Mono<ResponseEntity<Object>> save(@PathVariable String id,
|
||||
@RequestBody RouteDefinition route) {
|
||||
|
||||
return Mono.just(route).filter(this::validateRouteDefinition)
|
||||
.flatMap(routeDefinition -> this.routeDefinitionWriter
|
||||
.save(Mono.just(routeDefinition).map(r -> {
|
||||
r.setId(id);
|
||||
log.debug("Saving route: " + route);
|
||||
return r;
|
||||
}))
|
||||
.then(Mono.defer(() -> Mono.just(ResponseEntity
|
||||
.created(URI.create("/routes/" + id)).build()))))
|
||||
.switchIfEmpty(
|
||||
Mono.defer(() -> Mono.just(ResponseEntity.badRequest().build())));
|
||||
}
|
||||
|
||||
private boolean validateRouteDefinition(RouteDefinition routeDefinition) {
|
||||
boolean hasValidFilterDefinitions = routeDefinition.getFilters().stream()
|
||||
.allMatch(filterDefinition -> GatewayFilters.stream()
|
||||
.anyMatch(gatewayFilterFactory -> filterDefinition.getName()
|
||||
.equals(gatewayFilterFactory.name())));
|
||||
|
||||
boolean hasValidPredicateDefinitions = routeDefinition.getPredicates().stream()
|
||||
.allMatch(predicateDefinition -> routePredicates.stream()
|
||||
.anyMatch(routePredicate -> predicateDefinition.getName()
|
||||
.equals(routePredicate.name())));
|
||||
log.debug("FilterDefinitions valid: " + hasValidFilterDefinitions);
|
||||
log.debug("PredicateDefinitions valid: " + hasValidPredicateDefinitions);
|
||||
return hasValidFilterDefinitions && hasValidPredicateDefinitions;
|
||||
}
|
||||
|
||||
@DeleteMapping("/routes/{id}")
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEn
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
@@ -44,8 +45,10 @@ public class GatewayControllerEndpoint extends AbstractGatewayControllerEndpoint
|
||||
|
||||
public GatewayControllerEndpoint(List<GlobalFilter> globalFilters,
|
||||
List<GatewayFilterFactory> gatewayFilters,
|
||||
List<RoutePredicateFactory> routePredicates,
|
||||
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
|
||||
super(null, globalFilters, gatewayFilters, routeDefinitionWriter, routeLocator);
|
||||
super(null, globalFilters, gatewayFilters, routePredicates, routeDefinitionWriter,
|
||||
routeLocator);
|
||||
}
|
||||
|
||||
// TODO: Flush out routes without a definition
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEn
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
|
||||
@@ -45,8 +46,9 @@ public class GatewayLegacyControllerEndpoint extends AbstractGatewayControllerEn
|
||||
|
||||
public GatewayLegacyControllerEndpoint(RouteDefinitionLocator routeDefinitionLocator,
|
||||
List<GlobalFilter> globalFilters, List<GatewayFilterFactory> GatewayFilters,
|
||||
List<RoutePredicateFactory> routePredicates,
|
||||
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
|
||||
super(routeDefinitionLocator, globalFilters, GatewayFilters,
|
||||
super(routeDefinitionLocator, globalFilters, GatewayFilters, routePredicates,
|
||||
routeDefinitionWriter, routeLocator);
|
||||
}
|
||||
|
||||
|
||||
@@ -727,9 +727,10 @@ public class GatewayAutoConfiguration {
|
||||
public GatewayControllerEndpoint gatewayControllerEndpoint(
|
||||
List<GlobalFilter> globalFilters,
|
||||
List<GatewayFilterFactory> gatewayFilters,
|
||||
List<RoutePredicateFactory> routePredicates,
|
||||
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
|
||||
return new GatewayControllerEndpoint(globalFilters, gatewayFilters,
|
||||
routeDefinitionWriter, routeLocator);
|
||||
routePredicates, routeDefinitionWriter, routeLocator);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -739,9 +740,11 @@ public class GatewayAutoConfiguration {
|
||||
RouteDefinitionLocator routeDefinitionLocator,
|
||||
List<GlobalFilter> globalFilters,
|
||||
List<GatewayFilterFactory> gatewayFilters,
|
||||
List<RoutePredicateFactory> routePredicates,
|
||||
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
|
||||
return new GatewayLegacyControllerEndpoint(routeDefinitionLocator,
|
||||
globalFilters, gatewayFilters, routeDefinitionWriter, routeLocator);
|
||||
globalFilters, gatewayFilters, routePredicates, routeDefinitionWriter,
|
||||
routeLocator);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,8 +16,12 @@
|
||||
|
||||
package org.springframework.cloud.gateway.actuate;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.assertj.core.util.Maps;
|
||||
import org.junit.Test;
|
||||
@@ -28,13 +32,23 @@ import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.cloud.gateway.filter.FilterDefinition;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
|
||||
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
|
||||
import org.springframework.cloud.gateway.test.PermitAllSecurityConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
@@ -93,6 +107,92 @@ public class GatewayControllerEndpointTests {
|
||||
Maps.newHashMap("name", "value")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRouteFilters() {
|
||||
testClient.get()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routefilters")
|
||||
.exchange().expectStatus().isOk().expectBody(Map.class)
|
||||
.consumeWith(result -> {
|
||||
Map<?, ?> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).isNotEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoutePredicates() {
|
||||
testClient.get()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routepredicates")
|
||||
.exchange().expectStatus().isOk().expectBody(Map.class)
|
||||
.consumeWith(result -> {
|
||||
Map<?, ?> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).isNotEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostValidRouteDefinition() {
|
||||
|
||||
RouteDefinition testRouteDefinition = new RouteDefinition();
|
||||
testRouteDefinition.setUri(URI.create("http://example.org"));
|
||||
|
||||
FilterDefinition prefixPathFilterDefinition = new FilterDefinition(
|
||||
"PrefixPath=/test-path");
|
||||
FilterDefinition redirectToFilterDefinition = new FilterDefinition(
|
||||
"RemoveResponseHeader=Sensitive-Header");
|
||||
FilterDefinition testFilterDefinition = new FilterDefinition("TestFilter");
|
||||
testRouteDefinition.setFilters(Arrays.asList(prefixPathFilterDefinition,
|
||||
redirectToFilterDefinition, testFilterDefinition));
|
||||
|
||||
PredicateDefinition hostRoutePredicateDefinition = new PredicateDefinition(
|
||||
"Host=myhost.org");
|
||||
PredicateDefinition methodRoutePredicateDefinition = new PredicateDefinition(
|
||||
"Method=GET");
|
||||
PredicateDefinition testPredicateDefinition = new PredicateDefinition(
|
||||
"Test=value");
|
||||
testRouteDefinition.setPredicates(Arrays.asList(hostRoutePredicateDefinition,
|
||||
methodRoutePredicateDefinition, testPredicateDefinition));
|
||||
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/test-route")
|
||||
.accept(MediaType.APPLICATION_JSON_UTF8)
|
||||
.body(BodyInserters.fromObject(testRouteDefinition)).exchange()
|
||||
.expectStatus().isCreated();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostRouteWithNotExistingFilter() {
|
||||
|
||||
RouteDefinition testRouteDefinition = new RouteDefinition();
|
||||
testRouteDefinition.setUri(URI.create("http://example.org"));
|
||||
|
||||
FilterDefinition filterDefinition = new FilterDefinition(
|
||||
"NotExistingFilter=test-config");
|
||||
testRouteDefinition.setFilters(Collections.singletonList(filterDefinition));
|
||||
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/test-route")
|
||||
.accept(MediaType.APPLICATION_JSON_UTF8)
|
||||
.body(BodyInserters.fromObject(testRouteDefinition)).exchange()
|
||||
.expectStatus().isBadRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostRouteWithNotExistingPredicate() {
|
||||
|
||||
RouteDefinition testRouteDefinition = new RouteDefinition();
|
||||
testRouteDefinition.setUri(URI.create("http://example.org"));
|
||||
|
||||
PredicateDefinition predicateDefinition = new PredicateDefinition(
|
||||
"NotExistingPredicate=test-config");
|
||||
testRouteDefinition.setPredicates(Collections.singletonList(predicateDefinition));
|
||||
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/test-route")
|
||||
.accept(MediaType.APPLICATION_JSON_UTF8)
|
||||
.body(BodyInserters.fromObject(testRouteDefinition)).exchange()
|
||||
.expectStatus().isBadRequest();
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@EnableAutoConfiguration
|
||||
@Import(PermitAllSecurityConfiguration.class)
|
||||
@@ -106,6 +206,39 @@ public class GatewayControllerEndpointTests {
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestFilterGatewayFilterFactory customGatewayFilterFactory() {
|
||||
return new TestFilterGatewayFilterFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestRoutePredicateFactory customGatewayPredicateFactory() {
|
||||
return new TestRoutePredicateFactory(Object.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class TestFilterGatewayFilterFactory
|
||||
extends AbstractGatewayFilterFactory {
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(Object config) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class TestRoutePredicateFactory extends AbstractRoutePredicateFactory {
|
||||
|
||||
TestRoutePredicateFactory(Class configClass) {
|
||||
super(configClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ServerWebExchange> apply(Object config) {
|
||||
return (GatewayPredicate) serverWebExchange -> true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
provides: spring-cloud-gateway-core
|
||||
Reference in New Issue
Block a user