diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java index b9002441ef..eede460fa3 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java @@ -21,6 +21,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.time.ZonedDateTime; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -59,6 +60,7 @@ import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.LastHttpContent; +import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.handler.codec.http.TooLongHttpHeaderException; import io.netty.handler.codec.http.TooLongHttpLineException; import io.netty.handler.codec.http.cookie.Cookie; @@ -121,6 +123,7 @@ class HttpServerOperations extends HttpOperations> queryParamsMap; BiPredicate compressionPredicate; Function> paramsResolver; String path; @@ -459,6 +462,19 @@ public HttpHeaders requestHeaders() { throw new IllegalStateException("request not parsed"); } + @Override + public Map> queryParams() { + if (queryParamsMap == null) { + queryParamsMap = Collections.unmodifiableMap(parseQueryParams(this.nettyRequest)); + } + return queryParamsMap; + } + + private Map> parseQueryParams(HttpRequest request) { + QueryStringDecoder decoder = new QueryStringDecoder(request.uri()); + return decoder.parameters(); + } + @Override public String scheme() { if (connectionInfo != null) { diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerRequest.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerRequest.java index 61e2026c4d..bc3f4868bc 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerRequest.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerRequest.java @@ -17,6 +17,7 @@ import java.net.InetSocketAddress; import java.time.ZonedDateTime; +import java.util.List; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; @@ -143,6 +144,13 @@ default Flux receiveContent() { */ HttpHeaders requestHeaders(); + /** + * return parsed and decoded query parameter name value pairs + * + * @return query parameters {@link Map<String,List<String>>} + */ + Map> queryParams(); + /** * Returns the inbound protocol and version. * diff --git a/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java b/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java index 38fccbc0ab..006891419c 100644 --- a/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java +++ b/reactor-netty-http/src/test/java/reactor/netty/http/server/HttpServerTests.java @@ -2689,6 +2689,58 @@ void testUseComparatorOrderRoutes() { .verifyComplete(); } + @Test + void testRouteQuery() { + HttpServerRoutes serverRoutes = HttpServerRoutes.newRoutes() + .get("/yes/value", + (request, response) -> { + StringBuilder sbuf = new StringBuilder(); + request.queryParams().forEach((key, list) -> { + + for(String value : list) { + sbuf.append(key); + sbuf.append('='); + sbuf.append(value); + sbuf.append('&'); + } + sbuf.deleteCharAt(sbuf.length()-1); + }); + return response.sendString(Mono.just("/yes/value?" + sbuf)); + }).route(r -> true, (req, resp) -> { + return resp.sendString(Mono.just("/default")); + }); + + try { + disposableServer = HttpServer + .create() + .handle(serverRoutes) + .bindNow(); + + // verify HttpServerRequest has query parameters as the incoming request has query parameters + StepVerifier + .create(createClient(disposableServer.port()) + .get() + .uri("/yes/value?id=a&id=b") + .responseSingle((response, mono) -> mono.asString())) + .expectNext("/yes/value?id=a&id=b") + .verifyComplete(); + + // verify HttpServerRequest does not have query parameters if the http request does not have query + // parameters + StepVerifier + .create(createClient(disposableServer.port()) + .get() + .uri("/yes/value") + .responseSingle((response, mono) -> mono.asString())) + .expectNext("/yes/value?") + .verifyComplete(); + } finally { + if (disposableServer != null) { + disposableServer.disposeNow(); + } + } + } + @Test void testOverrideRouteOrder() { HttpServerRoutes serverRoutes = HttpServerRoutes.newRoutes()