diff --git a/spring-test/src/test/java/org/springframework/test/web/reactive/server/DefaultControllerSpecTests.java b/spring-test/src/test/java/org/springframework/test/web/reactive/server/DefaultControllerSpecTests.java index 33493792aa..c52657be34 100644 --- a/spring-test/src/test/java/org/springframework/test/web/reactive/server/DefaultControllerSpecTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/reactive/server/DefaultControllerSpecTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,15 +48,12 @@ public class DefaultControllerSpecTests { .exchange() .expectStatus().isOk() .expectBody(String.class).isEqualTo("Success"); - } - @Test - public void controllerEmptyPath() { new DefaultControllerSpec(new MyController()).build() .get().uri("") .exchange() .expectStatus().isOk() - .expectBody(String.class).isEqualTo("Success empty path"); + .expectBody(String.class).isEqualTo("Success"); } @Test @@ -121,19 +118,15 @@ public class DefaultControllerSpecTests { } + @SuppressWarnings("unused") @RestController private static class MyController { - @GetMapping("/") + @GetMapping public String handleRootPath() { return "Success"; } - @GetMapping - public String handleEmptyPath() { - return "Success empty path"; - } - @GetMapping("/exception") public void handleWithError() { throw new IllegalStateException(); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java index d7a54d578b..81ad4c26ad 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java @@ -89,8 +89,12 @@ public final class PatternsRequestCondition extends AbstractRequestCondition>> entry : this.pathPrefixes.entrySet()) { if (entry.getValue().test(handlerType)) { String prefix = entry.getKey(); diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestMappingIntegrationTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestMappingIntegrationTests.java index 9c0a07c627..7f879e5865 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestMappingIntegrationTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestMappingIntegrationTests.java @@ -61,6 +61,17 @@ class RequestMappingIntegrationTests extends AbstractRequestMappingIntegrationTe } + @ParameterizedHttpServerTest // gh-30293 + void emptyMapping(HttpServer httpServer) throws Exception { + startServer(httpServer); + + String url = "http://localhost:" + this.port; + assertThat(getRestTemplate().getForObject(url, String.class)).isEqualTo("root"); + + url += "/"; + assertThat(getRestTemplate().getForObject(url, String.class)).isEqualTo("root"); + } + @ParameterizedHttpServerTest void httpHead(HttpServer httpServer) throws Exception { startServer(httpServer); @@ -106,6 +117,11 @@ class RequestMappingIntegrationTests extends AbstractRequestMappingIntegrationTe @SuppressWarnings("unused") private static class TestRestController { + @GetMapping + public String get() { + return "root"; + } + @GetMapping("/text") public String textGet() { return "Foo"; diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java index a9c5725af6..e8e8135c98 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java @@ -255,6 +255,16 @@ public final class RequestMappingInfo implements RequestCondition condition = getActivePatternsCondition(); + return (condition instanceof PathPatternsRequestCondition pprc ? + pprc.isEmptyPathMapping() : ((PatternsRequestCondition) condition).isEmptyPathMapping()); + } + /** * Return the HTTP request methods of this {@link RequestMappingInfo}; * or instance with 0 request methods (never {@code null}). diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java index 411f96f8aa..d30fcc6a3a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -305,6 +305,9 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi if (typeInfo != null) { info = typeInfo.combine(info); } + if (info.isEmptyMapping()) { + info = info.mutate().paths("", "/").options(this.config).build(); + } String prefix = getPathPrefix(handlerType); if (prefix != null) { info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java index ff9e1f1e04..add09d33e8 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java @@ -192,7 +192,16 @@ class ServletAnnotationControllerHandlerMethodTests extends AbstractServletHandl request.setServletPath(""); MockHttpServletResponse response = new MockHttpServletResponse(); getServlet().service(request, response); + assertThat(response.getContentAsString()).isEqualTo("test"); + + // gh-30293 + request = new MockHttpServletRequest("GET", "/"); + response = new MockHttpServletResponse(); + getServlet().service(request, response); + + assertThat(response.getContentAsString()).isEqualTo("test"); + } @PathPatternsParameterizedTest