Add SetPath filter
This commit is contained in:
@@ -16,6 +16,7 @@ import org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.RemoveRequestHeaderFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.RemoveResponseHeaderFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.RewritePathFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.SetPathFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.SetResponseHeaderFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.SetStatusFilterFactory;
|
||||
import org.springframework.cloud.gateway.handler.GatewayFilteringWebHandler;
|
||||
@@ -140,6 +141,11 @@ public class GatewayAutoConfiguration {
|
||||
return new RewritePathFilterFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SetPathFilterFactory setPathFilterFactory() {
|
||||
return new SetPathFilterFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SetResponseHeaderFilterFactory setResponseHeaderFilterFactory() {
|
||||
return new SetResponseHeaderFilterFactory();
|
||||
|
||||
@@ -44,7 +44,7 @@ public interface GatewayFilter {
|
||||
*/
|
||||
Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain);
|
||||
|
||||
default <T> T getAttribute(ServerWebExchange exchange, String attributeName, Class<T> type) {
|
||||
static <T> T getAttribute(ServerWebExchange exchange, String attributeName, Class<T> type) {
|
||||
if (exchange.getAttributes().containsKey(attributeName)) {
|
||||
Object attr = exchange.getAttributes().get(attributeName);
|
||||
if (type.isAssignableFrom(attr.getClass())) {
|
||||
|
||||
@@ -10,6 +10,8 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import static org.springframework.cloud.gateway.filter.GatewayFilter.getAttribute;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.web.util.UriTemplate;
|
||||
|
||||
import static org.springframework.cloud.gateway.filter.GatewayFilter.getAttribute;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.UrlPredicateFactory.URL_PREDICATE_VARS_ATTR;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class SetPathFilterFactory implements FilterFactory {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public GatewayFilter apply(String template, String[] args) {
|
||||
UriTemplate uriTemplate = new UriTemplate(template);
|
||||
|
||||
//TODO: caching can happen here
|
||||
return (exchange, chain) -> {
|
||||
Map<String, String> variables = getAttribute(exchange, URL_PREDICATE_VARS_ATTR, Map.class);
|
||||
ServerHttpRequest req = exchange.getRequest();
|
||||
URI uri = uriTemplate.expand(variables);
|
||||
String newPath = uri.getPath();
|
||||
|
||||
ServerHttpRequest request = req.mutate()
|
||||
.path(newPath)
|
||||
.build();
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
@@ -12,6 +13,8 @@ import org.springframework.web.server.support.HttpRequestPathHelper;
|
||||
*/
|
||||
public class UrlPredicateFactory implements PredicateFactory {
|
||||
|
||||
public static final String URL_PREDICATE_VARS_ATTR = "urlPredicateVars";
|
||||
|
||||
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||
private HttpRequestPathHelper pathHelper = new HttpRequestPathHelper();
|
||||
|
||||
@@ -40,7 +43,12 @@ public class UrlPredicateFactory implements PredicateFactory {
|
||||
public Predicate<ServerWebExchange> apply(String pattern, String[] args) {
|
||||
return exchange -> {
|
||||
String lookupPath = getPathHelper().getLookupPathForRequest(exchange);
|
||||
return getPathMatcher().match(pattern, lookupPath);
|
||||
boolean match = getPathMatcher().match(pattern, lookupPath);
|
||||
if (match) {
|
||||
Map<String, String> variables = getPathMatcher().extractUriTemplateVariables(pattern, lookupPath);
|
||||
exchange.getAttributes().put(URL_PREDICATE_VARS_ATTR, variables);
|
||||
}
|
||||
return match;
|
||||
//TODO: support trailingSlashMatch
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.handler.predicate.UrlPredicateFactory;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class SetPathFilterFactoryTests {
|
||||
|
||||
@Test
|
||||
public void rewritePathFilterWorks() {
|
||||
HashMap<String, String> variables = new HashMap<>();
|
||||
testRewriteFilter("/baz/bar", "/foo/bar", "/baz/bar", variables);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setPathFilterWithTemplateVarsWorks() {
|
||||
HashMap<String, String> variables = new HashMap<>();
|
||||
variables.put("id", "123");
|
||||
testRewriteFilter("/bar/baz/{id}", "/foo/123", "/bar/baz/123", variables);
|
||||
}
|
||||
|
||||
private void testRewriteFilter(String template, String actualPath, String expectedPath, HashMap<String, String> variables) {
|
||||
GatewayFilter filter = new SetPathFilterFactory().apply(template, new String[]{});
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("http://localhost"+ actualPath)
|
||||
.build();
|
||||
|
||||
DefaultServerWebExchange exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse());
|
||||
exchange.getAttributes().put(UrlPredicateFactory.URL_PREDICATE_VARS_ATTR, variables);
|
||||
|
||||
WebFilterChain filterChain = mock(WebFilterChain.class);
|
||||
|
||||
ArgumentCaptor<ServerWebExchange> captor = ArgumentCaptor.forClass(ServerWebExchange.class);
|
||||
when(filterChain.filter(captor.capture())).thenReturn(Mono.empty());
|
||||
|
||||
filter.filter(exchange, filterChain);
|
||||
|
||||
ServerWebExchange webExchange = captor.getValue();
|
||||
|
||||
Assertions.assertThat(webExchange.getRequest().getURI().getPath()).isEqualTo(expectedPath);
|
||||
}
|
||||
}
|
||||
@@ -258,6 +258,26 @@ public class GatewayIntegrationTests {
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setPathFilterWorks() {
|
||||
Mono<ClientResponse> result = webClient.exchange(
|
||||
GET("http://localhost:" + port + "/foo/get")
|
||||
.header("Host", "www.setpath.org")
|
||||
.build()
|
||||
);
|
||||
|
||||
verify( () ->
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(
|
||||
response -> {
|
||||
HttpStatus statusCode = response.statusCode();
|
||||
assertThat(statusCode).isEqualTo(HttpStatus.OK);
|
||||
})
|
||||
.expectComplete()
|
||||
.verify(Duration.ofSeconds(3))
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setResponseHeaderFilterWorks() {
|
||||
Mono<ClientResponse> result = webClient.exchange(
|
||||
@@ -281,14 +301,14 @@ public class GatewayIntegrationTests {
|
||||
|
||||
@Test
|
||||
@Ignore("TODO: figure out how to set the status before response committed")
|
||||
public void setStatusStringWorks() {
|
||||
setStatusStringTest("www.setstatusstring.org", HttpStatus.BAD_REQUEST);
|
||||
public void setStatusIntWorks() {
|
||||
setStatusStringTest("www.setstatusint.org", HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("TODO: figure out how to set the status before response committed")
|
||||
public void setStatusIntWorks() {
|
||||
setStatusStringTest("www.setstatusint.org", HttpStatus.UNAUTHORIZED);
|
||||
public void setStatusStringWorks() {
|
||||
setStatusStringTest("www.setstatusstring.org", HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
private void setStatusStringTest(String host, HttpStatus status) {
|
||||
|
||||
@@ -57,6 +57,15 @@ spring:
|
||||
- AddResponseHeader=X-Request-Foo, Bar
|
||||
- RemoveResponseHeader=X-Request-Foo
|
||||
|
||||
# =====================================
|
||||
- id: set_path_test
|
||||
uri: http://httpbin.org:80
|
||||
predicates:
|
||||
- Host=**.setpath.org
|
||||
- Url=/foo/{segment}
|
||||
filters:
|
||||
- SetPath=/{segment}
|
||||
|
||||
# =====================================
|
||||
- id: set_response_header_test
|
||||
uri: http://httpbin.org:80
|
||||
@@ -68,15 +77,6 @@ spring:
|
||||
- AddResponseHeader=X-Request-Foo, Bar2
|
||||
- SetResponseHeader=X-Request-Foo, Bar
|
||||
|
||||
# =====================================
|
||||
- id: set_status_string_test
|
||||
uri: http://httpbin.org:80
|
||||
predicates:
|
||||
- Host=**.setstatusstring.org
|
||||
- Url=/headers
|
||||
filters:
|
||||
- SetStatus=BAD_REQUEST
|
||||
|
||||
# =====================================
|
||||
- id: set_status_int_test
|
||||
uri: http://httpbin.org:80
|
||||
@@ -86,6 +86,15 @@ spring:
|
||||
filters:
|
||||
- SetStatus=401
|
||||
|
||||
# =====================================
|
||||
- id: set_status_string_test
|
||||
uri: http://httpbin.org:80
|
||||
predicates:
|
||||
- Host=**.setstatusstring.org
|
||||
- Url=/headers
|
||||
filters:
|
||||
- SetStatus=BAD_REQUEST
|
||||
|
||||
# =====================================
|
||||
- id: rewrite_path_test
|
||||
uri: http://httpbin.org:80
|
||||
|
||||
Reference in New Issue
Block a user