diff --git a/spring-test/src/main/java/org/springframework/mock/http/server/reactive/MockServerHttpRequest.java b/spring-test/src/main/java/org/springframework/mock/http/server/reactive/MockServerHttpRequest.java index 487088c028..fedb569971 100644 --- a/spring-test/src/main/java/org/springframework/mock/http/server/reactive/MockServerHttpRequest.java +++ b/spring-test/src/main/java/org/springframework/mock/http/server/reactive/MockServerHttpRequest.java @@ -56,8 +56,6 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { private final HttpMethod httpMethod; - private final String contextPath; - private final MultiValueMap cookies; private final InetSocketAddress remoteAddress; @@ -70,9 +68,8 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { InetSocketAddress remoteAddress, Publisher body) { - super(uri, headers); + super(uri, contextPath, headers); this.httpMethod = httpMethod; - this.contextPath = contextPath; this.cookies = cookies; this.remoteAddress = remoteAddress; this.body = Flux.from(body); @@ -89,11 +86,6 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { return this.httpMethod.name(); } - @Override - public String getContextPath() { - return this.contextPath; - } - @Override public InetSocketAddress getRemoteAddress() { return this.remoteAddress; diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpRequest.java index 2e02359fb6..ba9c364532 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpRequest.java @@ -41,6 +41,8 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest { private final URI uri; + private final RequestPath path; + private final HttpHeaders headers; private MultiValueMap queryParams; @@ -51,10 +53,12 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest { /** * Constructor with the URI and headers for the request. * @param uri the URI for the request + * @param contextPath the context path for the request * @param headers the headers for the request */ - public AbstractServerHttpRequest(URI uri, HttpHeaders headers) { + public AbstractServerHttpRequest(URI uri, String contextPath, HttpHeaders headers) { this.uri = uri; + this.path = new DefaultRequestPath(uri, contextPath, StandardCharsets.UTF_8); this.headers = HttpHeaders.readOnlyHttpHeaders(headers); } @@ -64,6 +68,11 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest { return this.uri; } + @Override + public RequestPath getPath() { + return this.path; + } + @Override public HttpHeaders getHeaders() { return this.headers; diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/ContextPathCompositeHandler.java b/spring-web/src/main/java/org/springframework/http/server/reactive/ContextPathCompositeHandler.java index 98a27177a2..a5ff651b56 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/ContextPathCompositeHandler.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/ContextPathCompositeHandler.java @@ -16,7 +16,7 @@ import org.springframework.util.Assert; *

This is intended as a coarse-grained mechanism for delegating requests to * one of several applications -- each represented by an {@code HttpHandler}, with * the application "context path" (the prefix-based mapping) exposed via - * {@link ServerHttpRequest#getContextPath()}. + * {@link ServerHttpRequest#getPath()}. * * @author Rossen Stoyanchev * @since 5.0 @@ -49,12 +49,12 @@ public class ContextPathCompositeHandler implements HttpHandler { @Override public Mono handle(ServerHttpRequest request, ServerHttpResponse response) { // Remove underlying context path first (e.g. Servlet container) - String path = request.getPathWithinApplication(); + String path = request.getPath().pathWithinApplication().value(); return this.handlerMap.entrySet().stream() .filter(entry -> path.startsWith(entry.getKey())) .findFirst() .map(entry -> { - String contextPath = request.getContextPath() + entry.getKey(); + String contextPath = request.getPath().contextPath().value() + entry.getKey(); ServerHttpRequest newRequest = request.mutate().contextPath(contextPath).build(); return entry.getValue().handle(newRequest, response); }) diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/DefaultRequestPath.java b/spring-web/src/main/java/org/springframework/http/server/reactive/DefaultRequestPath.java index 38d02e7d0b..a6eb339c76 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/DefaultRequestPath.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/DefaultRequestPath.java @@ -58,6 +58,11 @@ class DefaultRequestPath implements RequestPath { this.pathWithinApplication = initPathWithinApplication(this.fullPath, this.contextPath); } + DefaultRequestPath(RequestPath requestPath, String contextPath, Charset charset) { + this.fullPath = new DefaultPathSegmentContainer(requestPath.value(), requestPath.pathSegments()); + this.contextPath = initContextPath(this.fullPath, contextPath); + this.pathWithinApplication = initPathWithinApplication(this.fullPath, this.contextPath); + } private static PathSegmentContainer parsePath(String path, Charset charset) { path = StringUtils.hasText(path) ? path : ""; diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/DefaultServerHttpRequestBuilder.java b/spring-web/src/main/java/org/springframework/http/server/reactive/DefaultServerHttpRequestBuilder.java index c203377b24..d22bf3454f 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/DefaultServerHttpRequestBuilder.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/DefaultServerHttpRequestBuilder.java @@ -18,6 +18,7 @@ package org.springframework.http.server.reactive; import java.net.URI; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -79,18 +80,51 @@ class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder { @Override public ServerHttpRequest build() { - URI uri = null; - if (this.path != null) { - uri = this.delegate.getURI(); - try { - uri = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), - this.path, uri.getQuery(), uri.getFragment()); - } - catch (URISyntaxException ex) { - throw new IllegalStateException("Invalid URI path: \"" + this.path + "\""); - } + URI uriToUse = getUriToUse(); + RequestPath path = getRequestPathToUse(uriToUse); + HttpHeaders headers = getHeadersToUse(); + return new MutativeDecorator(this.delegate, this.httpMethod, uriToUse, path, headers); + } + + @Nullable + private URI getUriToUse() { + if (this.path == null) { + return null; + } + URI uri = this.delegate.getURI(); + try { + return new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), + this.path, uri.getQuery(), uri.getFragment()); + } + catch (URISyntaxException ex) { + throw new IllegalStateException("Invalid URI path: \"" + this.path + "\""); + } + } + + @Nullable + private RequestPath getRequestPathToUse(@Nullable URI uriToUse) { + if (uriToUse == null && this.contextPath == null) { + return null; + } + else if (uriToUse == null) { + return new DefaultRequestPath(this.delegate.getPath(), this.contextPath, StandardCharsets.UTF_8); + } + else { + return new DefaultRequestPath(uriToUse, this.contextPath, StandardCharsets.UTF_8); + } + } + + @Nullable + private HttpHeaders getHeadersToUse() { + if (this.httpHeaders != null) { + HttpHeaders headers = new HttpHeaders(); + headers.putAll(this.delegate.getHeaders()); + headers.putAll(this.httpHeaders); + return headers; + } + else { + return null; } - return new MutativeDecorator(this.delegate, this.httpMethod, uri, this.contextPath, this.httpHeaders); } @@ -104,25 +138,20 @@ class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder { private final URI uri; - private final String contextPath; + private final RequestPath requestPath; private final HttpHeaders httpHeaders; - public MutativeDecorator(ServerHttpRequest delegate, HttpMethod httpMethod, - @Nullable URI uri, String contextPath, @Nullable HttpHeaders httpHeaders) { + + public MutativeDecorator(ServerHttpRequest delegate, HttpMethod method, + @Nullable URI uri, @Nullable RequestPath requestPath, + @Nullable HttpHeaders httpHeaders) { super(delegate); - this.httpMethod = httpMethod; + this.httpMethod = method; this.uri = uri; - this.contextPath = contextPath; - if (httpHeaders != null) { - this.httpHeaders = new HttpHeaders(); - this.httpHeaders.putAll(super.getHeaders()); - this.httpHeaders.putAll(httpHeaders); - } - else { - this.httpHeaders = null; - } + this.requestPath = requestPath; + this.httpHeaders = httpHeaders; } @Override @@ -136,8 +165,8 @@ class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder { } @Override - public String getContextPath() { - return (this.contextPath != null ? this.contextPath : super.getContextPath()); + public RequestPath getPath() { + return (this.requestPath != null ? this.requestPath : super.getPath()); } @Override diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorServerHttpRequest.java index 5c73b2db4e..d73e44b2df 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorServerHttpRequest.java @@ -49,7 +49,7 @@ public class ReactorServerHttpRequest extends AbstractServerHttpRequest { public ReactorServerHttpRequest(HttpServerRequest request, NettyDataBufferFactory bufferFactory) throws URISyntaxException { - super(initUri(request), initHeaders(request)); + super(initUri(request), "", initHeaders(request)); Assert.notNull(bufferFactory, "'bufferFactory' must not be null"); this.request = request; this.bufferFactory = bufferFactory; diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/RequestPath.java b/spring-web/src/main/java/org/springframework/http/server/reactive/RequestPath.java index 5db4e87df7..a6e8531425 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/RequestPath.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/RequestPath.java @@ -24,7 +24,12 @@ package org.springframework.http.server.reactive; public interface RequestPath extends PathSegmentContainer { /** - * The contextPath portion of the request if any. + * Returns the portion of the URL path that represents the application. + * The context path is always at the beginning of the path and starts but + * does not end with "/". It is shared for URLs of the same application. + *

The context path may come from the underlying runtime API such as + * when deploying as a WAR to a Servlet container or it may also be assigned + * through the use of {@link ContextPathCompositeHandler} or both. */ PathSegmentContainer contextPath(); diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/ServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/reactive/ServerHttpRequest.java index 76d6161ccc..d20bfda78b 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/ServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/ServerHttpRequest.java @@ -24,7 +24,6 @@ import org.springframework.http.HttpRequest; import org.springframework.http.ReactiveHttpInputMessage; import org.springframework.lang.Nullable; import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; /** * Represents a reactive server-side HTTP request @@ -36,35 +35,11 @@ import org.springframework.util.StringUtils; public interface ServerHttpRequest extends HttpRequest, ReactiveHttpInputMessage { /** - * Returns the portion of the URL path that represents the application. - * The context path is always at the beginning of the path and starts but - * does not end with "/". It is shared for URLs of the same application. - *

The context path may come from the underlying runtime API such as - * when deploying as a WAR to a Servlet container or it may also be assigned - * through the use of {@link ContextPathCompositeHandler} or both. - *

The context path is not decoded. - * @return the context path (not decoded) or an empty string + * Returns a structured representation of the request path including the + * context path + path within application portions, path segments with + * encoded and decoded values, and path parameters. */ - default String getContextPath() { - return ""; - } - - /** - * Returns the portion of the URL path after the {@link #getContextPath() - * contextPath}. The returned path is not decoded. - * @return the path under the contextPath - */ - default String getPathWithinApplication() { - String path = getURI().getRawPath(); - String contextPath = getContextPath(); - if (StringUtils.hasText(contextPath)) { - int length = contextPath.length(); - return (path.length() > length ? path.substring(length) : ""); - } - else { - return path; - } - } + RequestPath getPath(); /** * Return a read-only map with parsed and decoded query parameter values. @@ -104,12 +79,13 @@ public interface ServerHttpRequest extends HttpRequest, ReactiveHttpInputMessage Builder method(HttpMethod httpMethod); /** - * Set the request URI to return. + * Set the path to use instead of the {@code "rawPath"} of + * {@link ServerHttpRequest#getURI()}. */ Builder path(String path); /** - * Set the contextPath to return. + * Set the contextPath to use. */ Builder contextPath(String contextPath); diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/ServerHttpRequestDecorator.java b/spring-web/src/main/java/org/springframework/http/server/reactive/ServerHttpRequestDecorator.java index 422c9eb35f..8bac7782e8 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/ServerHttpRequestDecorator.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/ServerHttpRequestDecorator.java @@ -68,6 +68,11 @@ public class ServerHttpRequestDecorator implements ServerHttpRequest { return getDelegate().getURI(); } + @Override + public RequestPath getPath() { + return getDelegate().getPath(); + } + @Override public MultiValueMap getQueryParams() { return getDelegate().getQueryParams(); diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java index 072c0edd38..3fe9b83aa5 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java @@ -72,7 +72,7 @@ public class ServletServerHttpRequest extends AbstractServerHttpRequest { public ServletServerHttpRequest(HttpServletRequest request, AsyncContext asyncContext, DataBufferFactory bufferFactory, int bufferSize) throws IOException { - super(initUri(request), initHeaders(request)); + super(initUri(request), request.getContextPath(), initHeaders(request)); Assert.notNull(bufferFactory, "'bufferFactory' must not be null"); Assert.isTrue(bufferSize > 0, "'bufferSize' must be higher than 0"); @@ -154,11 +154,6 @@ public class ServletServerHttpRequest extends AbstractServerHttpRequest { return getServletRequest().getMethod(); } - @Override - public String getContextPath() { - return getServletRequest().getContextPath(); - } - @Override protected MultiValueMap initCookies() { MultiValueMap httpCookies = new LinkedMultiValueMap<>(); diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpRequest.java index 4c61a98633..f43e0aaf54 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpRequest.java @@ -53,7 +53,7 @@ public class UndertowServerHttpRequest extends AbstractServerHttpRequest { public UndertowServerHttpRequest(HttpServerExchange exchange, DataBufferFactory bufferFactory) { - super(initUri(exchange), initHeaders(exchange)); + super(initUri(exchange), "", initHeaders(exchange)); this.exchange = exchange; this.body = new RequestBodyPublisher(exchange, bufferFactory); this.body.registerListeners(exchange); diff --git a/spring-web/src/main/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSource.java b/spring-web/src/main/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSource.java index d59c1e8427..395f229832 100644 --- a/spring-web/src/main/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSource.java +++ b/spring-web/src/main/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSource.java @@ -80,7 +80,7 @@ public class UrlBasedCorsConfigurationSource implements CorsConfigurationSource @Override public CorsConfiguration getCorsConfiguration(ServerWebExchange exchange) { - String lookupPath = exchange.getRequest().getPathWithinApplication(); + String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value(); for (Map.Entry entry : this.corsConfigurations.entrySet()) { if (this.pathMatcher.match(entry.getKey(), lookupPath)) { return entry.getValue(); diff --git a/spring-web/src/test/java/org/springframework/http/server/reactive/ContextPathCompositeHandlerTests.java b/spring-web/src/test/java/org/springframework/http/server/reactive/ContextPathCompositeHandlerTests.java index 89b5e29265..26055d5dbd 100644 --- a/spring-web/src/test/java/org/springframework/http/server/reactive/ContextPathCompositeHandlerTests.java +++ b/spring-web/src/test/java/org/springframework/http/server/reactive/ContextPathCompositeHandlerTests.java @@ -105,7 +105,7 @@ public class ContextPathCompositeHandlerTests { new ContextPathCompositeHandler(map).handle(request, new MockServerHttpResponse()); assertTrue(handler.wasInvoked()); - assertEquals("/yet/another/path", handler.getRequest().getContextPath()); + assertEquals("/yet/another/path", handler.getRequest().getPath().contextPath().value()); } @Test @@ -133,7 +133,7 @@ public class ContextPathCompositeHandlerTests { private void assertInvoked(TestHttpHandler handler, String contextPath) { assertTrue(handler.wasInvoked()); - assertEquals(contextPath, handler.getRequest().getContextPath()); + assertEquals(contextPath, handler.getRequest().getPath().contextPath().value()); } private void assertNotInvoked(TestHttpHandler... handlers) { diff --git a/spring-web/src/test/java/org/springframework/http/server/reactive/RxNettyServerHttpRequest.java b/spring-web/src/test/java/org/springframework/http/server/reactive/RxNettyServerHttpRequest.java index ee528e1dc3..0e6a677fa6 100644 --- a/spring-web/src/test/java/org/springframework/http/server/reactive/RxNettyServerHttpRequest.java +++ b/spring-web/src/test/java/org/springframework/http/server/reactive/RxNettyServerHttpRequest.java @@ -56,7 +56,7 @@ public class RxNettyServerHttpRequest extends AbstractServerHttpRequest { NettyDataBufferFactory dataBufferFactory, InetSocketAddress remoteAddress) throws URISyntaxException { - super(initUri(request, remoteAddress), initHeaders(request)); + super(initUri(request, remoteAddress), "", initHeaders(request)); this.request = request; Assert.notNull(dataBufferFactory, "NettyDataBufferFactory must not be null"); diff --git a/spring-web/src/test/java/org/springframework/mock/http/server/reactive/test/MockServerHttpRequest.java b/spring-web/src/test/java/org/springframework/mock/http/server/reactive/test/MockServerHttpRequest.java index 99b3c9822a..ad1156aabf 100644 --- a/spring-web/src/test/java/org/springframework/mock/http/server/reactive/test/MockServerHttpRequest.java +++ b/spring-web/src/test/java/org/springframework/mock/http/server/reactive/test/MockServerHttpRequest.java @@ -56,8 +56,6 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { private final HttpMethod httpMethod; - private final String contextPath; - private final MultiValueMap cookies; private final InetSocketAddress remoteAddress; @@ -70,9 +68,8 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { InetSocketAddress remoteAddress, Publisher body) { - super(uri, headers); + super(uri, contextPath, headers); this.httpMethod = httpMethod; - this.contextPath = (contextPath != null ? contextPath : ""); this.cookies = cookies; this.remoteAddress = remoteAddress; this.body = Flux.from(body); @@ -89,11 +86,6 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { return this.httpMethod.name(); } - @Override - public String getContextPath() { - return this.contextPath; - } - @Override public InetSocketAddress getRemoteAddress() { return this.remoteAddress; diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java b/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java index 408f6cb18c..388eb091cb 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java @@ -100,7 +100,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping { @Override public Mono getHandlerInternal(ServerWebExchange exchange) { - String lookupPath = exchange.getRequest().getPathWithinApplication(); + String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value(); Object handler; try { handler = lookupHandler(lookupPath, exchange); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceUrlProvider.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceUrlProvider.java index 856c9c9582..58627c3db8 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceUrlProvider.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceUrlProvider.java @@ -166,7 +166,7 @@ public class ResourceUrlProvider implements ApplicationListener matches = getMatchingPatterns(lookupPath); return matches.isEmpty() ? null : @@ -243,7 +243,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition patternComparator = this.pathMatcher.getPatternComparator(lookupPath); Iterator iterator = this.patterns.iterator(); Iterator iteratorOther = other.patterns.iterator(); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java index 5ce446927e..d2be1d6127 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java @@ -257,7 +257,7 @@ public abstract class AbstractHandlerMethodMapping extends AbstractHandlerMap */ @Override public Mono getHandlerInternal(ServerWebExchange exchange) { - String lookupPath = exchange.getRequest().getPathWithinApplication(); + String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value(); if (logger.isDebugEnabled()) { logger.debug("Looking up handler method for path " + lookupPath); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RedirectView.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RedirectView.java index 057c48bf67..592ca19d12 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RedirectView.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RedirectView.java @@ -202,9 +202,11 @@ public class RedirectView extends AbstractUrlBasedView { String url = getUrl(); Assert.state(url != null, "'url' not set"); + ServerHttpRequest request = exchange.getRequest(); + StringBuilder targetUrl = new StringBuilder(); if (isContextRelative() && url.startsWith("/")) { - targetUrl.append(exchange.getRequest().getContextPath()); + targetUrl.append(request.getPath().contextPath().value()); } targetUrl.append(url); @@ -214,7 +216,7 @@ public class RedirectView extends AbstractUrlBasedView { } if (isPropagateQuery()) { - targetUrl = appendCurrentRequestQuery(targetUrl.toString(), exchange.getRequest()); + targetUrl = appendCurrentRequestQuery(targetUrl.toString(), request); } String result = targetUrl.toString(); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RequestContext.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RequestContext.java index 52fa5bdaf1..dafb4ae15f 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RequestContext.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RequestContext.java @@ -28,6 +28,7 @@ import org.springframework.context.NoSuchMessageException; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.StringUtils; import org.springframework.validation.BindException; import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; @@ -180,10 +181,10 @@ public class RequestContext { /** * Return the context path of the current web application. This is * useful for building links to other resources within the application. - *

Delegates to {@link ServerHttpRequest#getContextPath()}. + *

Delegates to {@link ServerHttpRequest#getPath()}. */ public String getContextPath() { - return this.exchange.getRequest().getContextPath(); + return this.exchange.getRequest().getPath().contextPath().value(); } /** @@ -193,7 +194,7 @@ public class RequestContext { * absolute path also URL-encoded accordingly */ public String getContextUrl(String relativeUrl) { - String url = getContextPath() + relativeUrl; + String url = StringUtils.applyRelativePath(getContextPath() + "/", relativeUrl); return getExchange().getResponse().encodeUrl(url); } @@ -208,7 +209,7 @@ public class RequestContext { * absolute path also URL-encoded accordingly */ public String getContextUrl(String relativeUrl, Map params) { - String url = getContextPath() + relativeUrl; + String url = StringUtils.applyRelativePath(getContextPath() + "/", relativeUrl); UriTemplate template = new UriTemplate(url); url = template.expand(params).toASCIIString(); return getExchange().getResponse().encodeUrl(url); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java index de88bb1c16..20a6514e78 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java @@ -258,7 +258,7 @@ public class ViewResolutionResultHandler extends HandlerResultHandlerSupport * Use the request path the leading and trailing slash stripped. */ private String getDefaultViewName(ServerWebExchange exchange) { - String path = exchange.getRequest().getPathWithinApplication(); + String path = exchange.getRequest().getPath().pathWithinApplication().value(); if (path.startsWith("/")) { path = path.substring(1); } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMappingTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMappingTests.java index f377599417..e42b4e365f 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMappingTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMappingTests.java @@ -213,7 +213,7 @@ public class RequestMappingInfoHandlerMappingTests { @SuppressWarnings("unchecked") public void handleMatchUriTemplateVariables() throws Exception { ServerWebExchange exchange = get("/1/2").toExchange(); - String lookupPath = exchange.getRequest().getPathWithinApplication(); + String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value(); RequestMappingInfo key = paths("/{path1}/{path2}").build(); this.handlerMapping.handleMatch(key, lookupPath, exchange); @@ -232,7 +232,7 @@ public class RequestMappingInfoHandlerMappingTests { URI url = URI.create("/group/a%2Fb"); ServerWebExchange exchange = method(HttpMethod.GET, url).toExchange(); - String lookupPath = exchange.getRequest().getPathWithinApplication(); + String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value(); this.handlerMapping.handleMatch(key, lookupPath, exchange); String name = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE; @@ -248,7 +248,7 @@ public class RequestMappingInfoHandlerMappingTests { public void handleMatchBestMatchingPatternAttribute() throws Exception { RequestMappingInfo key = paths("/{path1}/2", "/**").build(); ServerWebExchange exchange = get("/1/2").toExchange(); - String lookupPath = exchange.getRequest().getPathWithinApplication(); + String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value(); this.handlerMapping.handleMatch(key, lookupPath, exchange); assertEquals("/{path1}/2", exchange.getAttributes().get(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)); @@ -258,7 +258,7 @@ public class RequestMappingInfoHandlerMappingTests { public void handleMatchBestMatchingPatternAttributeNoPatternsDefined() throws Exception { RequestMappingInfo key = paths().build(); ServerWebExchange exchange = get("/1/2").toExchange(); - String lookupPath = exchange.getRequest().getPathWithinApplication(); + String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value(); this.handlerMapping.handleMatch(key, lookupPath, exchange); assertEquals("/1/2", exchange.getAttributes().get(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)); @@ -367,7 +367,7 @@ public class RequestMappingInfoHandlerMappingTests { private void handleMatch(ServerWebExchange exchange, String pattern) { RequestMappingInfo info = paths(pattern).build(); - String lookupPath = exchange.getRequest().getPathWithinApplication(); + String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value(); this.handlerMapping.handleMatch(info, lookupPath, exchange); } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ContextPathIntegrationTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ContextPathIntegrationTests.java index bb7c0036b5..50349f48cf 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ContextPathIntegrationTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ContextPathIntegrationTests.java @@ -117,7 +117,7 @@ public class ContextPathIntegrationTests { @GetMapping("/test") public String handle(ServerHttpRequest request) { - return "Tested in " + request.getContextPath(); + return "Tested in " + request.getPath().contextPath().value(); } } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/RedirectViewTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/RedirectViewTests.java index d0ba047b50..f373d501be 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/RedirectViewTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/RedirectViewTests.java @@ -47,7 +47,7 @@ public class RedirectViewTests { @Before public void setup() { - this.exchange = MockServerHttpRequest.get("/").contextPath("/context").toExchange(); + this.exchange = MockServerHttpRequest.get("/context/path").contextPath("/context").toExchange(); } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/RequestContextTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/RequestContextTests.java index 1e764b576c..5bf9da3f6f 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/RequestContextTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/RequestContextTests.java @@ -34,7 +34,8 @@ import static org.junit.Assert.assertEquals; */ public class RequestContextTests { - private final MockServerWebExchange exchange = MockServerHttpRequest.get("/").contextPath("foo/").toExchange(); + private final MockServerWebExchange exchange = MockServerHttpRequest.get("/foo/path") + .contextPath("/foo").toExchange(); private GenericApplicationContext applicationContext; @@ -50,7 +51,7 @@ public class RequestContextTests { @Test public void testGetContextUrl() throws Exception { RequestContext context = new RequestContext(this.exchange, this.model, this.applicationContext); - assertEquals("foo/bar", context.getContextUrl("bar")); + assertEquals("/foo/bar", context.getContextUrl("bar")); } @Test @@ -59,7 +60,7 @@ public class RequestContextTests { Map map = new HashMap<>(); map.put("foo", "bar"); map.put("spam", "bucket"); - assertEquals("foo/bar?spam=bucket", context.getContextUrl("{foo}?spam={spam}", map)); + assertEquals("/foo/bar?spam=bucket", context.getContextUrl("{foo}?spam={spam}", map)); } @Test @@ -68,7 +69,7 @@ public class RequestContextTests { Map map = new HashMap<>(); map.put("foo", "bar baz"); map.put("spam", "&bucket="); - assertEquals("foo/bar%20baz?spam=%26bucket%3D", context.getContextUrl("{foo}?spam={spam}", map)); + assertEquals("/foo/bar%20baz?spam=%26bucket%3D", context.getContextUrl("{foo}?spam={spam}", map)); } }