Expose RequestPath in ServerHttpRequest
The new structured getPath() method replaces the existing getContextPath() + getPathWithinApplication(). Issue: SPR-15648
This commit is contained in:
@@ -41,6 +41,8 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest {
|
||||
|
||||
private final URI uri;
|
||||
|
||||
private final RequestPath path;
|
||||
|
||||
private final HttpHeaders headers;
|
||||
|
||||
private MultiValueMap<String, String> 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;
|
||||
|
||||
@@ -16,7 +16,7 @@ import org.springframework.util.Assert;
|
||||
* <p>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<Void> 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);
|
||||
})
|
||||
|
||||
@@ -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 : "";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
* <p>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();
|
||||
|
||||
|
||||
@@ -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.
|
||||
* <p>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.
|
||||
* <p>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);
|
||||
|
||||
|
||||
@@ -68,6 +68,11 @@ public class ServerHttpRequestDecorator implements ServerHttpRequest {
|
||||
return getDelegate().getURI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestPath getPath() {
|
||||
return getDelegate().getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiValueMap<String, String> getQueryParams() {
|
||||
return getDelegate().getQueryParams();
|
||||
|
||||
@@ -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<String, HttpCookie> initCookies() {
|
||||
MultiValueMap<String, HttpCookie> httpCookies = new LinkedMultiValueMap<>();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<String, CorsConfiguration> entry : this.corsConfigurations.entrySet()) {
|
||||
if (this.pathMatcher.match(entry.getKey(), lookupPath)) {
|
||||
return entry.getValue();
|
||||
|
||||
Reference in New Issue
Block a user