From 19ce68d2d8cb6f62aa242ce550c6d7ed6893c321 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Sun, 18 Feb 2018 19:53:57 +0000 Subject: [PATCH] Prefix endpoint's path to controller endpoint mappings with no path Previously, if a controll endpoint included a mapping with no path, it would be mapped to the root of the application rather than the root of the endpoint's path. This commit updates the handler mappings for both reactive and Servlet web applications to treat a mapping with no paths as being a mapping with a single empty path. This ensures that a path with the endpoint's path prepended is used for the mapping. Closes gh-12104 --- .../ControllerEndpointHandlerMapping.java | 6 +++++ .../ControllerEndpointHandlerMapping.java | 4 ++++ ...ControllerEndpointHandlerMappingTests.java | 24 +++++++++++++++++++ ...ControllerEndpointHandlerMappingTests.java | 24 +++++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/ControllerEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/ControllerEndpointHandlerMapping.java index 1d007859ca..342bb22505 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/ControllerEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/ControllerEndpointHandlerMapping.java @@ -17,8 +17,10 @@ package org.springframework.boot.actuate.endpoint.web.reactive; import java.lang.reflect.Method; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; @@ -92,6 +94,10 @@ public class ControllerEndpointHandlerMapping extends RequestMappingHandlerMappi private RequestMappingInfo withEndpointMappedPatterns( ExposableControllerEndpoint endpoint, RequestMappingInfo mapping) { Set patterns = mapping.getPatternsCondition().getPatterns(); + if (patterns.isEmpty()) { + patterns = new HashSet( + Arrays.asList(getPathPatternParser().parse(""))); + } PathPattern[] endpointMappedPatterns = patterns.stream() .map((pattern) -> getEndpointMappedPattern(endpoint, pattern)) .toArray(PathPattern[]::new); diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/ControllerEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/ControllerEndpointHandlerMapping.java index 57490c901e..a8a169b0a6 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/ControllerEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/ControllerEndpointHandlerMapping.java @@ -19,6 +19,7 @@ package org.springframework.boot.actuate.endpoint.web.servlet; import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -93,6 +94,9 @@ public class ControllerEndpointHandlerMapping extends RequestMappingHandlerMappi private RequestMappingInfo withEndpointMappedPatterns( ExposableControllerEndpoint endpoint, RequestMappingInfo mapping) { Set patterns = mapping.getPatternsCondition().getPatterns(); + if (patterns.isEmpty()) { + patterns = new HashSet<>(Collections.singletonList("")); + } String[] endpointMappedPatterns = patterns.stream() .map((pattern) -> getEndpointMappedPattern(endpoint, pattern)) .toArray(String[]::new); diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/reactive/ControllerEndpointHandlerMappingTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/reactive/ControllerEndpointHandlerMappingTests.java index 555155dab9..c789cb0b63 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/reactive/ControllerEndpointHandlerMappingTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/reactive/ControllerEndpointHandlerMappingTests.java @@ -78,6 +78,16 @@ public class ControllerEndpointHandlerMappingTests { assertThat(getHandler(mapping, HttpMethod.GET, "/second")).isNull(); } + @Test + public void mappingWithNoPath() throws Exception { + ExposableControllerEndpoint pathless = pathlessEndpoint(); + ControllerEndpointHandlerMapping mapping = createMapping("actuator", pathless); + assertThat(getHandler(mapping, HttpMethod.GET, "/actuator/pathless")) + .isEqualTo(handlerOf(pathless.getController(), "get")); + assertThat(getHandler(mapping, HttpMethod.GET, "/pathless")).isNull(); + assertThat(getHandler(mapping, HttpMethod.GET, "/")).isNull(); + } + @Test public void mappingNarrowedToMethod() throws Exception { ExposableControllerEndpoint first = firstEndpoint(); @@ -118,6 +128,10 @@ public class ControllerEndpointHandlerMappingTests { return mockEndpoint("second", new SecondTestMvcEndpoint()); } + private ExposableControllerEndpoint pathlessEndpoint() { + return mockEndpoint("pathless", new PathlessControllerEndpoint()); + } + private ExposableControllerEndpoint mockEndpoint(String id, Object controller) { ExposableControllerEndpoint endpoint = mock(ExposableControllerEndpoint.class); given(endpoint.getId()).willReturn(id); @@ -146,4 +160,14 @@ public class ControllerEndpointHandlerMappingTests { } + @ControllerEndpoint(id = "pathless") + private static class PathlessControllerEndpoint { + + @GetMapping + public String get() { + return "test"; + } + + } + } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/ControllerEndpointHandlerMappingTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/ControllerEndpointHandlerMappingTests.java index 7b18f684e4..acdc57b70b 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/ControllerEndpointHandlerMappingTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/servlet/ControllerEndpointHandlerMappingTests.java @@ -84,6 +84,16 @@ public class ControllerEndpointHandlerMappingTests { mapping.getHandler(request("POST", "/actuator/first")); } + @Test + public void mappingWithNoPath() throws Exception { + ExposableControllerEndpoint pathless = pathlessEndpoint(); + ControllerEndpointHandlerMapping mapping = createMapping("actuator", pathless); + assertThat(mapping.getHandler(request("GET", "/actuator/pathless")).getHandler()) + .isEqualTo(handlerOf(pathless.getController(), "get")); + assertThat(mapping.getHandler(request("GET", "/pathless"))).isNull(); + assertThat(mapping.getHandler(request("GET", "/"))).isNull(); + } + private ControllerEndpointHandlerMapping createMapping(String prefix, ExposableControllerEndpoint... endpoints) { ControllerEndpointHandlerMapping mapping = new ControllerEndpointHandlerMapping( @@ -110,6 +120,10 @@ public class ControllerEndpointHandlerMappingTests { return mockEndpoint("second", new SecondTestMvcEndpoint()); } + private ExposableControllerEndpoint pathlessEndpoint() { + return mockEndpoint("pathless", new PathlessControllerEndpoint()); + } + private ExposableControllerEndpoint mockEndpoint(String id, Object controller) { ExposableControllerEndpoint endpoint = mock(ExposableControllerEndpoint.class); given(endpoint.getId()).willReturn(id); @@ -138,4 +152,14 @@ public class ControllerEndpointHandlerMappingTests { } + @ControllerEndpoint(id = "pathless") + private static class PathlessControllerEndpoint { + + @GetMapping + public String get() { + return "test"; + } + + } + }