Merge pull request #50095 from dlwldnjs1009

* gh-50095:
  Always match the links endpoint with GET

Closes gh-50095
This commit is contained in:
Andy Wilkinson
2026-04-30 13:43:20 +01:00
4 changed files with 42 additions and 15 deletions
@@ -316,6 +316,8 @@ public final class EndpointRequest {
/**
* Restricts the matcher to only consider requests with a particular http method.
* <p>
* The links endpoint, if included, is always matched using {@code GET}.
* @param httpMethod the http method to include
* @return a copy of the matcher further restricted to only match requests with
* the specified http method
@@ -373,9 +375,9 @@ public final class EndpointRequest {
String linksPath = getLinksPath(properties.getBasePath());
if (linksPath != null) {
List<ServerWebExchangeMatcher> linksMatchers = new ArrayList<>();
linksMatchers.add(new PathPatternParserServerWebExchangeMatcher(linksPath));
linksMatchers.add(new PathPatternParserServerWebExchangeMatcher(linksPath, HttpMethod.GET));
if (!linksPath.endsWith("/")) {
linksMatchers.add(new PathPatternParserServerWebExchangeMatcher(linksPath + "/"));
linksMatchers.add(new PathPatternParserServerWebExchangeMatcher(linksPath + "/", HttpMethod.GET));
}
return new OrServerWebExchangeMatcher(linksMatchers);
}
@@ -227,9 +227,9 @@ public final class EndpointRequest {
protected List<RequestMatcher> getLinksMatchers(RequestMatcherFactory requestMatcherFactory,
RequestMatcherProvider matcherProvider, String linksPath) {
List<RequestMatcher> linksMatchers = new ArrayList<>();
linksMatchers.add(requestMatcherFactory.antPath(matcherProvider, null, linksPath));
linksMatchers.add(requestMatcherFactory.antPath(matcherProvider, HttpMethod.GET, linksPath));
if (!linksPath.endsWith("/")) {
linksMatchers.add(requestMatcherFactory.antPath(matcherProvider, null, linksPath, "/"));
linksMatchers.add(requestMatcherFactory.antPath(matcherProvider, HttpMethod.GET, linksPath, "/"));
}
return linksMatchers;
}
@@ -350,6 +350,8 @@ public final class EndpointRequest {
/**
* Restricts the matcher to only consider requests with a particular HTTP method.
* <p>
* The links endpoint, if included, is always matched using {@code GET}.
* @param httpMethod the HTTP method to include
* @return a copy of the matcher further restricted to only match requests with
* the specified HTTP method
@@ -75,6 +75,15 @@ class EndpointRequestTests {
assertMatcher(matcher, "/actuator").doesNotMatch(HttpMethod.GET, "/actuator/foo");
}
@Test
void toAnyEndpointWithHttpMethodShouldUseGetForLinks() {
ServerWebExchangeMatcher matcher = EndpointRequest.toAnyEndpoint().withHttpMethod(HttpMethod.POST);
assertMatcher(matcher, "/actuator").matches(HttpMethod.GET, "/actuator");
assertMatcher(matcher, "/actuator").doesNotMatch(HttpMethod.POST, "/actuator");
assertMatcher(matcher, "/actuator").matches(HttpMethod.GET, "/actuator/");
assertMatcher(matcher, "/actuator").doesNotMatch(HttpMethod.POST, "/actuator/");
}
@Test
void toAnyEndpointShouldMatchEndpointPathWithTrailingSlash() {
ServerWebExchangeMatcher matcher = EndpointRequest.toAnyEndpoint();
@@ -140,8 +149,10 @@ class EndpointRequestTests {
ServerWebExchangeMatcher matcher = EndpointRequest.toLinks();
assertMatcher(matcher).doesNotMatch("/actuator/foo");
assertMatcher(matcher).doesNotMatch("/actuator/bar");
assertMatcher(matcher).matches("/actuator");
assertMatcher(matcher).matches("/actuator/");
assertMatcher(matcher).matches(HttpMethod.GET, "/actuator");
assertMatcher(matcher).doesNotMatch(HttpMethod.POST, "/actuator");
assertMatcher(matcher).matches(HttpMethod.GET, "/actuator/");
assertMatcher(matcher).doesNotMatch(HttpMethod.POST, "/actuator/");
}
@Test
@@ -64,7 +64,7 @@ class EndpointRequestTests {
assertMatcher(matcher, "/actuator").matches("/actuator/foo/zoo/");
assertMatcher(matcher, "/actuator").matches("/actuator/bar");
assertMatcher(matcher, "/actuator").matches("/actuator/bar/baz");
assertMatcher(matcher, "/actuator").matches("/actuator");
assertMatcher(matcher, "/actuator").matches(HttpMethod.GET, "/actuator");
}
@Test
@@ -75,12 +75,22 @@ class EndpointRequestTests {
assertMatcher(matcher, "/actuator").doesNotMatch(HttpMethod.GET, "/actuator/foo");
}
@Test
void toAnyEndpointWithHttpMethodShouldUseGetForLinks() {
EndpointRequest.EndpointRequestMatcher matcher = EndpointRequest.toAnyEndpoint()
.withHttpMethod(HttpMethod.POST);
assertMatcher(matcher, "/actuator").matches(HttpMethod.GET, "/actuator");
assertMatcher(matcher, "/actuator").doesNotMatch(HttpMethod.POST, "/actuator");
assertMatcher(matcher, "/actuator").matches(HttpMethod.GET, "/actuator/");
assertMatcher(matcher, "/actuator").doesNotMatch(HttpMethod.POST, "/actuator/");
}
@Test
void toAnyEndpointShouldMatchEndpointPathWithTrailingSlash() {
RequestMatcher matcher = EndpointRequest.toAnyEndpoint();
assertMatcher(matcher, "/actuator").matches("/actuator/foo/");
assertMatcher(matcher, "/actuator").matches("/actuator/bar/");
assertMatcher(matcher, "/actuator").matches("/actuator/");
assertMatcher(matcher, "/actuator").matches(HttpMethod.GET, "/actuator/");
}
@Test
@@ -97,7 +107,7 @@ class EndpointRequestTests {
RequestMatcher matcher = EndpointRequest.toAnyEndpoint();
RequestMatcherAssert assertMatcher = assertMatcher(matcher, mockPathMappedEndpoints(""), null,
WebServerNamespace.MANAGEMENT);
assertMatcher.matches("/");
assertMatcher.matches(HttpMethod.GET, "/");
assertMatcher.matches("/foo");
}
@@ -112,7 +122,7 @@ class EndpointRequestTests {
RequestMatcher matcher = EndpointRequest.toAnyEndpoint();
assertMatcher(matcher, "/actuator").matches("/actuator/foo");
assertMatcher(matcher, "/actuator").matches("/actuator/bar");
assertMatcher(matcher, "/actuator").matches("/actuator");
assertMatcher(matcher, "/actuator").matches(HttpMethod.GET, "/actuator");
assertMatcher(matcher, "/actuator").doesNotMatch("/actuator/baz");
}
@@ -147,8 +157,10 @@ class EndpointRequestTests {
RequestMatcher matcher = EndpointRequest.toLinks();
assertMatcher(matcher).doesNotMatch("/actuator/foo");
assertMatcher(matcher).doesNotMatch("/actuator/bar");
assertMatcher(matcher).matches("/actuator");
assertMatcher(matcher).matches("/actuator/");
assertMatcher(matcher).matches(HttpMethod.GET, "/actuator");
assertMatcher(matcher).doesNotMatch(HttpMethod.POST, "/actuator");
assertMatcher(matcher).matches(HttpMethod.GET, "/actuator/");
assertMatcher(matcher).doesNotMatch(HttpMethod.POST, "/actuator/");
}
@Test
@@ -165,7 +177,7 @@ class EndpointRequestTests {
RequestMatcher matcher = EndpointRequest.toLinks();
RequestMatcherAssert assertMatcher = assertMatcher(matcher, mockPathMappedEndpoints(""), null,
WebServerNamespace.MANAGEMENT);
assertMatcher.matches("/");
assertMatcher.matches(HttpMethod.GET, "/");
assertMatcher.doesNotMatch("/foo");
}
@@ -180,7 +192,7 @@ class EndpointRequestTests {
assertMatcher(matcher, pathMappedEndpoints).doesNotMatch("/actuator/foo");
assertMatcher(matcher, pathMappedEndpoints).doesNotMatch("/actuator/baz");
assertMatcher(matcher).matches("/actuator/bar");
assertMatcher(matcher).matches("/actuator");
assertMatcher(matcher).matches(HttpMethod.GET, "/actuator");
}
@Test
@@ -195,7 +207,7 @@ class EndpointRequestTests {
RequestMatcher matcher = EndpointRequest.toAnyEndpoint().excluding("foo");
assertMatcher(matcher).doesNotMatch("/actuator/foo");
assertMatcher(matcher).matches("/actuator/bar");
assertMatcher(matcher).matches("/actuator");
assertMatcher(matcher).matches(HttpMethod.GET, "/actuator");
}
@Test