Add support for changing context path in ServletRequestPath
This commit implements modifyContextPath in ServletRequestPath and apply the same logic of concatenating the servlet path with the context path. Closes gh-33251
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -41,6 +41,7 @@ import org.springframework.util.StringUtils;
|
|||||||
* {@link org.springframework.util.PathMatcher} otherwise.
|
* {@link org.springframework.util.PathMatcher} otherwise.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
|
* @author Stephane Nicoll
|
||||||
* @since 5.3
|
* @since 5.3
|
||||||
*/
|
*/
|
||||||
public abstract class ServletRequestPathUtils {
|
public abstract class ServletRequestPathUtils {
|
||||||
@@ -186,14 +187,16 @@ public abstract class ServletRequestPathUtils {
|
|||||||
*/
|
*/
|
||||||
private static final class ServletRequestPath implements RequestPath {
|
private static final class ServletRequestPath implements RequestPath {
|
||||||
|
|
||||||
|
private final PathElements pathElements;
|
||||||
|
|
||||||
private final RequestPath requestPath;
|
private final RequestPath requestPath;
|
||||||
|
|
||||||
private final PathContainer contextPath;
|
private final PathContainer contextPath;
|
||||||
|
|
||||||
private ServletRequestPath(String rawPath, @Nullable String contextPath, String servletPathPrefix) {
|
private ServletRequestPath(PathElements pathElements) {
|
||||||
Assert.notNull(servletPathPrefix, "`servletPathPrefix` is required");
|
this.pathElements = pathElements;
|
||||||
this.requestPath = RequestPath.parse(rawPath, contextPath + servletPathPrefix);
|
this.requestPath = pathElements.createRequestPath();
|
||||||
this.contextPath = PathContainer.parsePath(StringUtils.hasText(contextPath) ? contextPath : "");
|
this.contextPath = pathElements.createContextPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -218,7 +221,7 @@ public abstract class ServletRequestPathUtils {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RequestPath modifyContextPath(String contextPath) {
|
public RequestPath modifyContextPath(String contextPath) {
|
||||||
throw new UnsupportedOperationException();
|
return new ServletRequestPath(this.pathElements.withContextPath(contextPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -249,7 +252,7 @@ public abstract class ServletRequestPathUtils {
|
|||||||
requestUri = (requestUri != null ? requestUri : request.getRequestURI());
|
requestUri = (requestUri != null ? requestUri : request.getRequestURI());
|
||||||
String servletPathPrefix = getServletPathPrefix(request);
|
String servletPathPrefix = getServletPathPrefix(request);
|
||||||
return (StringUtils.hasText(servletPathPrefix) ?
|
return (StringUtils.hasText(servletPathPrefix) ?
|
||||||
new ServletRequestPath(requestUri, request.getContextPath(), servletPathPrefix) :
|
new ServletRequestPath(new PathElements(requestUri, request.getContextPath(), servletPathPrefix)) :
|
||||||
RequestPath.parse(requestUri, request.getContextPath()));
|
RequestPath.parse(requestUri, request.getContextPath()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,6 +268,38 @@ public abstract class ServletRequestPathUtils {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
record PathElements(String rawPath, @Nullable String contextPath, String servletPathPrefix) {
|
||||||
|
|
||||||
|
PathElements {
|
||||||
|
Assert.notNull(servletPathPrefix, "`servletPathPrefix` is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
private RequestPath createRequestPath() {
|
||||||
|
return RequestPath.parse(this.rawPath, this.contextPath + this.servletPathPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PathContainer createContextPath() {
|
||||||
|
return PathContainer.parsePath(StringUtils.hasText(this.contextPath) ? this.contextPath : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
PathElements withContextPath(String contextPath) {
|
||||||
|
if (!contextPath.startsWith("/") || contextPath.endsWith("/")) {
|
||||||
|
throw new IllegalArgumentException("Invalid contextPath '" + contextPath + "': " +
|
||||||
|
"must start with '/' and not end with '/'");
|
||||||
|
}
|
||||||
|
String contextPathToUse = this.servletPathPrefix + contextPath;
|
||||||
|
if (StringUtils.hasText(this.contextPath())) {
|
||||||
|
throw new IllegalStateException("Could not change context path to '" + contextPathToUse +
|
||||||
|
"': a context path is already specified");
|
||||||
|
}
|
||||||
|
if (!this.rawPath.startsWith(contextPathToUse)) {
|
||||||
|
throw new IllegalArgumentException("Invalid contextPath '" + contextPathToUse + "': " +
|
||||||
|
"must match the start of requestPath: '" + this.rawPath + "'");
|
||||||
|
}
|
||||||
|
return new PathElements(this.rawPath, contextPathToUse, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,11 +24,14 @@ import org.springframework.web.testfixture.servlet.MockHttpServletMapping;
|
|||||||
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
|
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link ServletRequestPathUtils}.
|
* Tests for {@link ServletRequestPathUtils}.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
|
* @author Stephane Nicoll
|
||||||
*/
|
*/
|
||||||
class ServletRequestPathUtilsTests {
|
class ServletRequestPathUtilsTests {
|
||||||
|
|
||||||
@@ -47,19 +50,63 @@ class ServletRequestPathUtilsTests {
|
|||||||
testParseAndCache("/app/servlet/a//", "/app", "/servlet", "/a//");
|
testParseAndCache("/app/servlet/a//", "/app", "/servlet", "/a//");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void modifyPathContextWithExistingContextPath() {
|
||||||
|
RequestPath requestPath = createRequestPath("/app/api/persons/42", "/app", "/api", "/persons/42");
|
||||||
|
assertThatIllegalStateException().isThrownBy(() -> requestPath.modifyContextPath("/persons"))
|
||||||
|
.withMessage("Could not change context path to '/api/persons': a context path is already specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void modifyPathContextWhenContextPathIsNotInThePath() {
|
||||||
|
RequestPath requestPath = createRequestPath("/api/persons/42", "", "/api", "/persons/42");
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> requestPath.modifyContextPath("/something"))
|
||||||
|
.withMessage("Invalid contextPath '/api/something': " +
|
||||||
|
"must match the start of requestPath: '/api/persons/42'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void modifyPathContextReplacesServletPath() {
|
||||||
|
RequestPath requestPath = createRequestPath("/api/persons/42", "", "/api", "/persons/42");
|
||||||
|
RequestPath updatedRequestPath = requestPath.modifyContextPath("/persons");
|
||||||
|
assertThat(updatedRequestPath.contextPath().value()).isEqualTo("/api/persons");
|
||||||
|
assertThat(updatedRequestPath.pathWithinApplication().value()).isEqualTo("/42");
|
||||||
|
assertThat(updatedRequestPath.value()).isEqualTo("/api/persons/42");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void modifyPathContextWithContextPathNotStartingWithSlash() {
|
||||||
|
RequestPath requestPath = createRequestPath("/api/persons/42", "", "/api", "/persons/42");
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> requestPath.modifyContextPath("persons"))
|
||||||
|
.withMessage("Invalid contextPath 'persons': must start with '/' and not end with '/'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void modifyPathContextWithContextPathEndingWithSlash() {
|
||||||
|
RequestPath requestPath = createRequestPath("/api/persons/42", "", "/api", "/persons/42");
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> requestPath.modifyContextPath("/persons/"))
|
||||||
|
.withMessage("Invalid contextPath '/persons/': must start with '/' and not end with '/'");
|
||||||
|
}
|
||||||
|
|
||||||
private void testParseAndCache(
|
private void testParseAndCache(
|
||||||
String requestUri, String contextPath, String servletPath, String pathWithinApplication) {
|
String requestUri, String contextPath, String servletPath, String pathWithinApplication) {
|
||||||
|
|
||||||
|
RequestPath requestPath = createRequestPath(requestUri, contextPath, servletPath, pathWithinApplication);
|
||||||
|
|
||||||
|
assertThat(requestPath.contextPath().value()).isEqualTo(contextPath);
|
||||||
|
assertThat(requestPath.pathWithinApplication().value()).isEqualTo(pathWithinApplication);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RequestPath createRequestPath(
|
||||||
|
String requestUri, String contextPath, String servletPath, String pathWithinApplication) {
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
|
||||||
request.setContextPath(contextPath);
|
request.setContextPath(contextPath);
|
||||||
request.setServletPath(servletPath);
|
request.setServletPath(servletPath);
|
||||||
request.setHttpServletMapping(new MockHttpServletMapping(
|
request.setHttpServletMapping(new MockHttpServletMapping(
|
||||||
pathWithinApplication, contextPath, "myServlet", MappingMatch.PATH));
|
pathWithinApplication, contextPath, "myServlet", MappingMatch.PATH));
|
||||||
|
|
||||||
RequestPath requestPath = ServletRequestPathUtils.parseAndCache(request);
|
return ServletRequestPathUtils.parseAndCache(request);
|
||||||
|
|
||||||
assertThat(requestPath.contextPath().value()).isEqualTo(contextPath);
|
|
||||||
assertThat(requestPath.pathWithinApplication().value()).isEqualTo(pathWithinApplication);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user