diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/ProxyExchangeHandlerFunction.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/ProxyExchangeHandlerFunction.java index 416793db99..67c655b42f 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/ProxyExchangeHandlerFunction.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/ProxyExchangeHandlerFunction.java @@ -29,6 +29,7 @@ import org.springframework.cloud.gateway.server.mvc.filter.HttpHeadersFilter.RequestHttpHeadersFilter; import org.springframework.cloud.gateway.server.mvc.filter.HttpHeadersFilter.ResponseHttpHeadersFilter; import org.springframework.http.HttpHeaders; +import org.springframework.util.MultiValueMap; import org.springframework.web.servlet.function.HandlerFunction; import org.springframework.web.servlet.function.ServerRequest; import org.springframework.web.servlet.function.ServerResponse; @@ -66,7 +67,7 @@ public ProxyExchangeHandlerFunction(ProxyExchange proxyExchange, @Override public ServerResponse handle(ServerRequest serverRequest) { URI uri = uriResolver.apply(serverRequest); - boolean encoded = containsEncodedQuery(serverRequest.uri()); + boolean encoded = containsEncodedQuery(serverRequest.uri(), serverRequest.params()); // @formatter:off URI url = UriComponentsBuilder.fromUri(serverRequest.uri()) .scheme(uri.getScheme()) @@ -113,14 +114,15 @@ private HttpHeaders filterHeaders(Stream> filters return filtered; } - private static boolean containsEncodedQuery(URI uri) { - boolean encoded = (uri.getRawQuery() != null && uri.getRawQuery().contains("%")) + private static boolean containsEncodedQuery(URI uri, MultiValueMap params) { + String rawQuery = uri.getRawQuery(); + boolean encoded = (rawQuery != null && rawQuery.contains("%")) || (uri.getRawPath() != null && uri.getRawPath().contains("%")); // Verify if it is really fully encoded. Treat partial encoded as unencoded. if (encoded) { try { - UriComponentsBuilder.fromUri(uri).build(true); + UriComponentsBuilder.fromUri(uri).replaceQueryParams(params).build(true); return true; } catch (IllegalArgumentException ignored) { @@ -132,7 +134,7 @@ private static boolean containsEncodedQuery(URI uri) { return false; } - return encoded; + return false; } public interface URIResolver extends Function { diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java index ee28356ebb..9f1fd415a7 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java @@ -670,6 +670,21 @@ public void queryParamWorks() { }); } + @SuppressWarnings("rawtypes") + @Test + public void queryParamWithSpecialCharactersWorks() { + restClient.get().uri("/get?myparam= &intlparam=æøå").exchange().expectStatus().isOk().expectBody(Map.class) + .consumeWith(result -> { + Map responseBody = result.getResponseBody(); + assertThat(responseBody).containsKey("args"); + Map args = getMap(responseBody, "args"); + assertThat(args).containsKey("myparam"); + assertThat(args.get("myparam")).isEqualTo(" "); + assertThat(args).containsKey("intlparam"); + assertThat(args.get("intlparam")).isEqualTo("æøå"); + }); + } + @SpringBootConfiguration @EnableAutoConfiguration @LoadBalancerClient(name = "httpbin", configuration = TestLoadBalancerConfig.Httpbin.class)