diff --git a/spring-cloud-gateway-server/pom.xml b/spring-cloud-gateway-server/pom.xml index c812ec6d..5ab1e3c6 100644 --- a/spring-cloud-gateway-server/pom.xml +++ b/spring-cloud-gateway-server/pom.xml @@ -309,7 +309,6 @@ org.apache.maven.plugins - maven-jar-plugin 3.3.0 diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RedirectToGatewayFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RedirectToGatewayFilterFactory.java index 87f7f7d0..cf9845e0 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RedirectToGatewayFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RedirectToGatewayFilterFactory.java @@ -30,6 +30,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.util.Assert; import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.util.UriComponentsBuilder; import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.setResponseStatus; @@ -50,40 +51,66 @@ public class RedirectToGatewayFilterFactory */ public static final String URL_KEY = "url"; + /** + * IncludeRequestParams key. + */ + public static final String INCLUDE_REQUEST_PARAMS_KEY = "includeRequestParams"; + public RedirectToGatewayFilterFactory() { super(Config.class); } @Override public List shortcutFieldOrder() { - return Arrays.asList(STATUS_KEY, URL_KEY); + return Arrays.asList(STATUS_KEY, URL_KEY, INCLUDE_REQUEST_PARAMS_KEY); } @Override public GatewayFilter apply(Config config) { - return apply(config.status, config.url); + return apply(config.status, config.url, config.includeRequestParams); } public GatewayFilter apply(String statusString, String urlString) { + return apply(statusString, urlString, false); + } + + public GatewayFilter apply(String statusString, String urlString, boolean includeRequestParams) { HttpStatusHolder httpStatus = HttpStatusHolder.parse(statusString); Assert.isTrue(httpStatus.is3xxRedirection(), "status must be a 3xx code, but was " + statusString); final URI url = URI.create(urlString); - return apply(httpStatus, url); + return apply(httpStatus, url, includeRequestParams); } public GatewayFilter apply(HttpStatus httpStatus, URI uri) { - return apply(new HttpStatusHolder(httpStatus, null), uri); + return apply(new HttpStatusHolder(httpStatus, null), uri, false); + } + + public GatewayFilter apply(HttpStatus httpStatus, URI uri, boolean includeRequestParams) { + return apply(new HttpStatusHolder(httpStatus, null), uri, includeRequestParams); } public GatewayFilter apply(HttpStatusHolder httpStatus, URI uri) { + return apply(httpStatus, uri, false); + } + + public GatewayFilter apply(HttpStatusHolder httpStatus, URI uri, boolean includeRequestParams) { return new GatewayFilter() { @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { if (!exchange.getResponse().isCommitted()) { setResponseStatus(exchange, httpStatus); + String location; + if (includeRequestParams) { + location = UriComponentsBuilder.fromUri(uri).queryParams(exchange.getRequest().getQueryParams()) + .build().toUri().toString(); + } + else { + location = uri.toString(); + } + final ServerHttpResponse response = exchange.getResponse(); - response.getHeaders().set(HttpHeaders.LOCATION, uri.toString()); + response.getHeaders().set(HttpHeaders.LOCATION, location); return response.setComplete(); } return Mono.empty(); @@ -98,7 +125,8 @@ public class RedirectToGatewayFilterFactory else { status = httpStatus.getStatus().toString(); } - return filterToStringCreator(RedirectToGatewayFilterFactory.this).append(status, uri).toString(); + return filterToStringCreator(RedirectToGatewayFilterFactory.this).append(status, uri) + .append(INCLUDE_REQUEST_PARAMS_KEY, includeRequestParams).toString(); } }; } @@ -109,6 +137,8 @@ public class RedirectToGatewayFilterFactory String url; + boolean includeRequestParams; + public String getStatus() { return status; } @@ -125,6 +155,14 @@ public class RedirectToGatewayFilterFactory this.url = url; } + public boolean isIncludeRequestParams() { + return includeRequestParams; + } + + public void setIncludeRequestParams(boolean includeRequestParams) { + this.includeRequestParams = includeRequestParams; + } + } } diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpec.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpec.java index 5b0b6aea..273be579 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpec.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpec.java @@ -471,6 +471,18 @@ public class GatewayFilterSpec extends UriSpec { return redirect(String.valueOf(status), url.toString()); } + /** + * A filter that will return a redirect response back to the client. + * @param status an HTTP status code, should be a {@code 300} series redirect + * @param url the URL to redirect to. This URL will be set in the {@code location} + * header + * @param includeRequestParams if true, query params will be passed to the url + * @return a {@link GatewayFilterSpec} that can be used to apply additional filters + */ + public GatewayFilterSpec redirect(int status, URI url, boolean includeRequestParams) { + return redirect(String.valueOf(status), url.toString(), includeRequestParams); + } + /** * A filter that will return a redirect response back to the client. * @param status an HTTP status code, should be a {@code 300} series redirect @@ -482,6 +494,18 @@ public class GatewayFilterSpec extends UriSpec { return redirect(String.valueOf(status), url); } + /** + * A filter that will return a redirect response back to the client. + * @param status an HTTP status code, should be a {@code 300} series redirect + * @param url the URL to redirect to. This URL will be set in the {@code location} + * header + * @param includeRequestParams if true, query params will be passed to the url + * @return a {@link GatewayFilterSpec} that can be used to apply additional filters + */ + public GatewayFilterSpec redirect(int status, String url, boolean includeRequestParams) { + return redirect(String.valueOf(status), url, includeRequestParams); + } + /** * A filter that will return a redirect response back to the client. * @param status an HTTP status code, should be a {@code 300} series redirect @@ -490,7 +514,19 @@ public class GatewayFilterSpec extends UriSpec { * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ public GatewayFilterSpec redirect(String status, URI url) { - return redirect(status, url.toString()); + return redirect(status, url.toString(), false); + } + + /** + * A filter that will return a redirect response back to the client. + * @param status an HTTP status code, should be a {@code 300} series redirect + * @param url the URL to redirect to. This URL will be set in the {@code location} + * header + * @param includeRequestParams if true, query params will be passed to the url + * @return a {@link GatewayFilterSpec} that can be used to apply additional filters + */ + public GatewayFilterSpec redirect(String status, String url, boolean includeRequestParams) { + return filter(getBean(RedirectToGatewayFilterFactory.class).apply(status, url, includeRequestParams)); } /** @@ -512,8 +548,21 @@ public class GatewayFilterSpec extends UriSpec { * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ public GatewayFilterSpec redirect(HttpStatus status, URL url) { + return redirect(status, url, false); + } + + /** + * A filter that will return a redirect response back to the client. + * @param status an HTTP status code, should be a {@code 300} series redirect + * @param url the URL to redirect to. This URL will be set in the {@code location} + * header + * @param includeRequestParams if true, query params will be passed to the url + * @return a {@link GatewayFilterSpec} that can be used to apply additional filters + */ + public GatewayFilterSpec redirect(HttpStatus status, URL url, boolean includeRequestParams) { try { - return filter(getBean(RedirectToGatewayFilterFactory.class).apply(status, url.toURI())); + return filter( + getBean(RedirectToGatewayFilterFactory.class).apply(status, url.toURI(), includeRequestParams)); } catch (URISyntaxException e) { throw new IllegalArgumentException("Invalid URL", e); diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RedirectToGatewayFilterFactoryTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RedirectToGatewayFilterFactoryTests.java index f9441fbb..21d8a220 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RedirectToGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RedirectToGatewayFilterFactoryTests.java @@ -47,6 +47,19 @@ public class RedirectToGatewayFilterFactoryTests extends BaseWebClientTests { .isEqualTo(HttpStatus.FOUND).expectHeader().valueEquals(HttpHeaders.LOCATION, "https://example.org"); } + @Test + public void redirectToUrlDoesNotPassQueryParametersByDefault() { + testClient.get().uri("/?membership=gold").header("Host", "www.redirectto.org").exchange().expectStatus() + .isEqualTo(HttpStatus.FOUND).expectHeader().valueEquals(HttpHeaders.LOCATION, "https://example.org"); + } + + @Test + public void redirectToUrlAddsQueryParametersWhenEnabledOnFilter() { + testClient.get().uri("/?membership=gold").header("Host", "queryparams.redirectto.org").exchange().expectStatus() + .isEqualTo(HttpStatus.FOUND).expectHeader() + .valueEquals(HttpHeaders.LOCATION, "https://example.org?membership=gold"); + } + @Test public void redirectToRelativeUrlFilterWorks() { testClient.get().uri("/").header("Host", "www.relativeredirect.org").exchange().expectStatus() @@ -59,13 +72,27 @@ public class RedirectToGatewayFilterFactoryTests extends BaseWebClientTests { .isEqualTo(HttpStatus.FOUND).expectHeader().valueEquals(HttpHeaders.LOCATION, "/index.html#/customers"); } + @Test + public void redirectToRelativeUrlDoesNotPassQueryParametersByDefault() { + testClient.get().uri("/?membership=gold").header("Host", "www.relativeredirect.org").exchange().expectStatus() + .isEqualTo(HttpStatus.FOUND).expectHeader().valueEquals(HttpHeaders.LOCATION, "/index.html#/customers"); + } + + @Test + public void redirectToRelativeUrlAddsQueryParametersWhenEnabledOnFilter() { + testClient.get().uri("/?membership=gold").header("Host", "queryparams.relativeredirect.org").exchange() + .expectStatus().isEqualTo(HttpStatus.FOUND).expectHeader() + .valueEquals(HttpHeaders.LOCATION, "/index.html?membership=gold#/customers"); + } + @Test public void toStringFormat() { Config config = new Config(); config.setStatus("301"); config.setUrl("http://newurl"); + config.setIncludeRequestParams(true); GatewayFilter filter = new RedirectToGatewayFilterFactory().apply(config); - assertThat(filter.toString()).contains("301").contains("http://newurl"); + assertThat(filter.toString()).contains("301").contains("http://newurl").contains("true"); } @EnableAutoConfiguration @@ -78,6 +105,9 @@ public class RedirectToGatewayFilterFactoryTests extends BaseWebClientTests { return builder.routes() .route("relative_redirect_uri_object", r -> r.host("strcode.relativeredirect.org") .filters(f -> f.redirect("302", URI.create("/index.html#/customers"))).uri("no://op")) + .route("relative_redirect_with_query_params", + r -> r.host("queryparams.relativeredirect.org") + .filters(f -> f.redirect(302, "/index.html#/customers", true)).uri("no://op")) .route("relative_redirect", r -> r.host("**.relativeredirect.org") .filters(f -> f.redirect(302, "/index.html#/customers")).uri("no://op")) .build(); diff --git a/spring-cloud-gateway-server/src/test/resources/application.yml b/spring-cloud-gateway-server/src/test/resources/application.yml index 2e5a0571..b031e413 100644 --- a/spring-cloud-gateway-server/src/test/resources/application.yml +++ b/spring-cloud-gateway-server/src/test/resources/application.yml @@ -296,6 +296,14 @@ spring: filters: - SetPath=/anything/{digits} + # ===================================== + - id: redirect_to_include_query_params_test + uri: ${test.uri} + predicates: + - Host=queryparams.redirectto.org + filters: + - RedirectTo=302, https://example.org, true + # ===================================== - id: redirect_to_test uri: ${test.uri}