Adds support for multiple HTTP methods in MethodRoutePredicate
Fixes gh-1473
This commit is contained in:
committed by
Spencer Gibb
parent
c9d3adae12
commit
73bebc9962
@@ -165,7 +165,7 @@ This predicate extracts the URI template variables (like `sub` defined in the ex
|
||||
|
||||
|
||||
=== Method Route Predicate Factory
|
||||
The Method Route Predicate Factory takes one parameter: the HTTP method to match.
|
||||
The Method Route Predicate Factory takes one or more parameters: the HTTP methods to match.
|
||||
|
||||
.application.yml
|
||||
[source,yaml]
|
||||
@@ -177,10 +177,10 @@ spring:
|
||||
- id: method_route
|
||||
uri: https://example.org
|
||||
predicates:
|
||||
- Method=GET
|
||||
- Method=GET,POST
|
||||
----
|
||||
|
||||
This route would match if the request method was a `GET`.
|
||||
This route would match if the request method was a `GET` or a `POST`.
|
||||
|
||||
=== Path Route Predicate Factory
|
||||
The Path Route Predicate Factory takes two parameter: a list of Spring `PathMatcher` patterns and an optional flag to `matchOptionalTrailingSeparator`.
|
||||
|
||||
@@ -21,10 +21,14 @@ import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static java.util.Arrays.stream;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Dennis Menge
|
||||
*/
|
||||
public class MethodRoutePredicateFactory
|
||||
extends AbstractRoutePredicateFactory<MethodRoutePredicateFactory.Config> {
|
||||
@@ -32,15 +36,26 @@ public class MethodRoutePredicateFactory
|
||||
/**
|
||||
* Method key.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String METHOD_KEY = "method";
|
||||
|
||||
/**
|
||||
* Methods key.
|
||||
*/
|
||||
public static final String METHODS_KEY = "methods";
|
||||
|
||||
public MethodRoutePredicateFactory() {
|
||||
super(Config.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> shortcutFieldOrder() {
|
||||
return Arrays.asList(METHOD_KEY);
|
||||
return Arrays.asList(METHODS_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShortcutType shortcutType() {
|
||||
return ShortcutType.GATHER_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,28 +64,42 @@ public class MethodRoutePredicateFactory
|
||||
@Override
|
||||
public boolean test(ServerWebExchange exchange) {
|
||||
HttpMethod requestMethod = exchange.getRequest().getMethod();
|
||||
return requestMethod == config.getMethod();
|
||||
return stream(config.getMethods())
|
||||
.anyMatch(httpMethod -> httpMethod == requestMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Method: %s", config.getMethod());
|
||||
return String.format("Methods: %s", Arrays.toString(config.getMethods()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Validated
|
||||
public static class Config {
|
||||
|
||||
private HttpMethod method;
|
||||
private HttpMethod[] methods;
|
||||
|
||||
@Deprecated
|
||||
public HttpMethod getMethod() {
|
||||
return method;
|
||||
if (methods != null && methods.length > 0) {
|
||||
return methods[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setMethod(HttpMethod method) {
|
||||
this.method = method;
|
||||
this.methods = new HttpMethod[] {method};
|
||||
}
|
||||
|
||||
public HttpMethod[] getMethods() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
public void setMethods(HttpMethod... methods) {
|
||||
this.methods = methods;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.springframework.cloud.gateway.support.ipresolver.RemoteAddressResolve
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static java.util.Arrays.stream;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.toAsyncPredicate;
|
||||
|
||||
/**
|
||||
@@ -147,22 +148,26 @@ public class PredicateSpec extends UriSpec {
|
||||
|
||||
/**
|
||||
* A predicate that checks if the HTTP method matches
|
||||
* @param method the name of the HTTP method
|
||||
* @param methods the name of the HTTP methods
|
||||
* @return a {@link BooleanSpec} to be used to add logical operators
|
||||
*/
|
||||
public BooleanSpec method(String method) {
|
||||
return asyncPredicate(getBean(MethodRoutePredicateFactory.class)
|
||||
.applyAsync(c -> c.setMethod(HttpMethod.resolve(method))));
|
||||
public BooleanSpec method(String... methods) {
|
||||
return asyncPredicate(getBean(MethodRoutePredicateFactory.class).applyAsync(c -> {
|
||||
HttpMethod[] httpMethods = stream(methods).map(HttpMethod::resolve)
|
||||
.toArray(HttpMethod[]::new);
|
||||
c.setMethods(httpMethods);
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* A predicate that checks if the HTTP method matches
|
||||
* @param method the HTTP method
|
||||
* @param methods the HTTP methods
|
||||
* @return a {@link BooleanSpec} to be used to add logical operators
|
||||
*/
|
||||
public BooleanSpec method(HttpMethod method) {
|
||||
return asyncPredicate(getBean(MethodRoutePredicateFactory.class)
|
||||
.applyAsync(c -> c.setMethod(method)));
|
||||
public BooleanSpec method(HttpMethod... methods) {
|
||||
return asyncPredicate(getBean(MethodRoutePredicateFactory.class).applyAsync(c -> {
|
||||
c.setMethods(methods);
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,14 +21,19 @@ import java.util.function.Predicate;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping;
|
||||
import org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactory.Config;
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@@ -46,15 +51,49 @@ public class MethodRoutePredicateFactoryTests extends BaseWebClientTests {
|
||||
.expectStatus().isOk().expectHeader()
|
||||
.valueEquals(HANDLER_MAPPER_HEADER,
|
||||
RoutePredicateHandlerMapping.class.getSimpleName())
|
||||
.expectHeader().valueEquals(ROUTE_ID_HEADER, "method_test");
|
||||
.expectHeader().valueEquals(ROUTE_ID_HEADER, "method_test_get");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toStringFormat() {
|
||||
public void methodGetAndPostRouteWorks() {
|
||||
testClient.post().uri("/multivalueheaders").header("Host", "www.method.org")
|
||||
.exchange().expectStatus().isOk().expectHeader()
|
||||
.valueEquals(HANDLER_MAPPER_HEADER,
|
||||
RoutePredicateHandlerMapping.class.getSimpleName())
|
||||
.expectHeader().valueEquals(ROUTE_ID_HEADER, "method_test_get_and_post");
|
||||
|
||||
testClient.get().uri("/multivalueheaders").header("Host", "www.method.org")
|
||||
.exchange().expectStatus().isOk().expectHeader()
|
||||
.valueEquals(HANDLER_MAPPER_HEADER,
|
||||
RoutePredicateHandlerMapping.class.getSimpleName())
|
||||
.expectHeader().valueEquals(ROUTE_ID_HEADER, "method_test_get_and_post");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodRouteNotMatching() {
|
||||
testClient.delete().uri("/multivalueheaders").header("Host", "www.method.org")
|
||||
.exchange().expectStatus()
|
||||
.value(integer -> integer.equals(HttpStatus.METHOD_NOT_ALLOWED))
|
||||
.expectHeader().valueEquals(HANDLER_MAPPER_HEADER,
|
||||
RoutePredicateHandlerMapping.class.getSimpleName()).expectHeader()
|
||||
/* Fallback to route with '/**' path predicate matches, see application.yml in test resources */
|
||||
.valueEquals(ROUTE_ID_HEADER, "default_path_to_httpbin");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toStringFormatSingleMethod() {
|
||||
Config config = new Config();
|
||||
config.setMethod(HttpMethod.GET);
|
||||
Predicate predicate = new MethodRoutePredicateFactory().apply(config);
|
||||
assertThat(predicate.toString()).contains("Method: " + config.getMethod());
|
||||
assertThat(predicate.toString()).contains("Methods: [GET]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toStringFormatMultipleMethods() {
|
||||
Config config = new Config();
|
||||
config.setMethods(HttpMethod.GET, HttpMethod.PUT);
|
||||
Predicate predicate = new MethodRoutePredicateFactory().apply(config);
|
||||
assertThat(predicate.toString()).contains("Methods: [GET, PUT]");
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@@ -62,6 +101,21 @@ public class MethodRoutePredicateFactoryTests extends BaseWebClientTests {
|
||||
@Import(DefaultTestConfig.class)
|
||||
public static class TestConfig {
|
||||
|
||||
@Value("${test.uri}")
|
||||
String uri;
|
||||
|
||||
@Bean
|
||||
public RouteLocator testRouteLocator(RouteLocatorBuilder builder) {
|
||||
return builder.routes()
|
||||
.route("method_test_get",
|
||||
r -> r.method("GET").and().path("/get")
|
||||
.filters(f -> f.prefixPath("/httpbin")).uri(uri))
|
||||
.route("method_test_get_and_post",
|
||||
r -> r.method("GET", "POST").and().path("/multivalueheaders")
|
||||
.filters(f -> f.prefixPath("/httpbin")).uri(uri))
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user