diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/HandlerMapping.java b/spring-webflux/src/main/java/org/springframework/web/reactive/HandlerMapping.java index 250654f7c4..064f7ddd5b 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/HandlerMapping.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/HandlerMapping.java @@ -31,29 +31,31 @@ import org.springframework.web.server.ServerWebExchange; public interface HandlerMapping { /** - * Name of the {@link ServerWebExchange} attribute that contains the - * best matching pattern within the handler mapping. - *

Note: This attribute is not required to be supported by all - * HandlerMapping implementations. URL-based HandlerMappings will - * typically support it, but handlers should not necessarily expect - * this request attribute to be present in all scenarios. + * Name of the {@link ServerWebExchange#getAttributes() attribute} that + * contains the mapped handler for the best matching pattern. + */ + String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler"; + + /** + * Name of the {@link ServerWebExchange#getAttributes() attribute} that + * contains the best matching pattern within the handler mapping. */ String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern"; /** - * Name of the {@link ServerWebExchange} attribute that contains the path - * within the handler mapping, in case of a pattern match, or the full - * relevant URI (typically within the DispatcherServlet's mapping) else. + * Name of the {@link ServerWebExchange#getAttributes() attribute} that + * contains the path within the handler mapping, in case of a pattern match + * such as {@code "/static/**"} or the full relevant URI otherwise. *

Note: This attribute is not required to be supported by all * HandlerMapping implementations. URL-based HandlerMappings will - * typically support it, but handlers should not necessarily expect + * typically support it but handlers should not necessarily expect * this request attribute to be present in all scenarios. */ String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping"; /** - * Name of the {@link ServerWebExchange} attribute that contains the URI - * templates map, mapping variable names to values. + * Name of the {@link ServerWebExchange#getAttributes() attribute} that + * contains the URI templates map mapping variable names to values. *

Note: This attribute is not required to be supported by all * HandlerMapping implementations. URL-based HandlerMappings will * typically support it, but handlers should not necessarily expect @@ -62,8 +64,9 @@ public interface HandlerMapping { String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables"; /** - * Name of the {@link ServerWebExchange} attribute that contains a map with - * URI matrix variables. + * Name of the {@link ServerWebExchange#getAttributes() attribute} that + * contains a map with URI variable names and a corresponding MultiValueMap + * of URI matrix variables for each. *

Note: This attribute is not required to be supported by all * HandlerMapping implementations and may also not be present depending on * whether the HandlerMapping is configured to keep matrix variable content @@ -72,8 +75,8 @@ public interface HandlerMapping { String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables"; /** - * Name of the {@link ServerWebExchange} attribute that contains the set of - * producible MediaTypes applicable to the mapped handler. + * Name of the {@link ServerWebExchange#getAttributes() attribute} containing + * the set of producible MediaType's applicable to the mapped handler. *

Note: This attribute is not required to be supported by all * HandlerMapping implementations. Handlers should not necessarily expect * this request attribute to be present in all scenarios. diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java b/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java index c40f05e012..dabb19812c 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java @@ -145,8 +145,9 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping { validateHandler(handler, exchange); - exchange.getAttributes().put(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping); + exchange.getAttributes().put(BEST_MATCHING_HANDLER_ATTRIBUTE, handler); exchange.getAttributes().put(BEST_MATCHING_PATTERN_ATTRIBUTE, bestMatch); + exchange.getAttributes().put(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping); return handler; } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java index 70314a347c..b075544191 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java @@ -322,7 +322,7 @@ public abstract class AbstractHandlerMethodMapping extends AbstractHandlerMap lookupPath + "': {" + m1 + ", " + m2 + "}"); } } - handleMatch(bestMatch.mapping, lookupPath, exchange); + handleMatch(bestMatch.mapping, bestMatch.handlerMethod, lookupPath, exchange); return bestMatch.handlerMethod; } else { @@ -342,10 +342,12 @@ public abstract class AbstractHandlerMethodMapping extends AbstractHandlerMap /** * Invoked when a matching mapping is found. * @param mapping the matching mapping + * @param handlerMethod the matching method * @param lookupPath the lookup path within the current mapping * @param exchange the current exchange */ - protected void handleMatch(T mapping, String lookupPath, ServerWebExchange exchange) { + protected void handleMatch(T mapping, HandlerMethod handlerMethod, String lookupPath, + ServerWebExchange exchange) { } /** diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMapping.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMapping.java index d98081058e..18b8a98ea2 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMapping.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMapping.java @@ -106,8 +106,10 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe * @see HandlerMapping#PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE */ @Override - protected void handleMatch(RequestMappingInfo info, String lookupPath, ServerWebExchange exchange) { - super.handleMatch(info, lookupPath, exchange); + protected void handleMatch(RequestMappingInfo info, HandlerMethod handlerMethod, String lookupPath, + ServerWebExchange exchange) { + + super.handleMatch(info, handlerMethod, lookupPath, exchange); PathPattern bestPattern; Map uriVariables; @@ -133,6 +135,7 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe )); } + exchange.getAttributes().put(BEST_MATCHING_HANDLER_ATTRIBUTE, handlerMethod); exchange.getAttributes().put(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern); exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriVariables); diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMappingTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMappingTests.java index b0b7a788d5..bc0a202e16 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMappingTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMappingTests.java @@ -39,6 +39,7 @@ import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest; import org.springframework.stereotype.Controller; +import org.springframework.util.ClassUtils; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PutMapping; @@ -60,6 +61,7 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; import static org.springframework.mock.http.server.reactive.test.MockServerHttpRequest.get; import static org.springframework.mock.http.server.reactive.test.MockServerHttpRequest.method; @@ -69,6 +71,8 @@ import static org.springframework.web.bind.annotation.RequestMethod.OPTIONS; import static org.springframework.web.method.MvcAnnotationPredicates.getMapping; import static org.springframework.web.method.MvcAnnotationPredicates.requestMapping; import static org.springframework.web.method.ResolvableMethod.on; +import static org.springframework.web.reactive.HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE; +import static org.springframework.web.reactive.HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE; import static org.springframework.web.reactive.result.method.RequestMappingInfo.paths; /** @@ -77,6 +81,9 @@ import static org.springframework.web.reactive.result.method.RequestMappingInfo. */ public class RequestMappingInfoHandlerMappingTests { + private static final HandlerMethod handlerMethod = new HandlerMethod(new TestController(), + ClassUtils.getMethod(TestController.class, "dummy")); + private TestRequestMappingInfoHandlerMapping handlerMapping; @@ -169,8 +176,8 @@ public class RequestMappingInfoHandlerMappingTests { Mono mono = this.handlerMapping.getHandler(exchange); assertError(mono, UnsupportedMediaTypeStatusException.class, - ex -> assertEquals("Response status 415 with reason \"Invalid mime type \"bogus\": does not contain '/'\"", - ex.getMessage())); + ex -> assertEquals("Response status 415 with reason \"Invalid mime type \"bogus\": " + + "does not contain '/'\"", ex.getMessage())); } @Test // SPR-8462 @@ -219,7 +226,7 @@ public class RequestMappingInfoHandlerMappingTests { String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value(); RequestMappingInfo key = paths("/{path1}/{path2}").build(); - this.handlerMapping.handleMatch(key, lookupPath, exchange); + this.handlerMapping.handleMatch(key, handlerMethod, lookupPath, exchange); String name = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE; Map uriVariables = (Map) exchange.getAttributes().get(name); @@ -236,7 +243,7 @@ public class RequestMappingInfoHandlerMappingTests { ServerWebExchange exchange = method(HttpMethod.GET, url).toExchange(); String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value(); - this.handlerMapping.handleMatch(key, lookupPath, exchange); + this.handlerMapping.handleMatch(key, handlerMethod, lookupPath, exchange); String name = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE; @SuppressWarnings("unchecked") @@ -252,11 +259,13 @@ public class RequestMappingInfoHandlerMappingTests { RequestMappingInfo key = paths("/{path1}/2", "/**").build(); ServerWebExchange exchange = get("/1/2").toExchange(); String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value(); - this.handlerMapping.handleMatch(key, lookupPath, exchange); + this.handlerMapping.handleMatch(key, handlerMethod, lookupPath, exchange); - PathPattern bestMatch = (PathPattern) exchange.getAttributes() - .get(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); + PathPattern bestMatch = (PathPattern) exchange.getAttributes().get(BEST_MATCHING_PATTERN_ATTRIBUTE); assertEquals("/{path1}/2", bestMatch.getPatternString()); + + HandlerMethod mapped = (HandlerMethod) exchange.getAttributes().get(BEST_MATCHING_HANDLER_ATTRIBUTE); + assertSame(handlerMethod, mapped); } @Test @@ -264,10 +273,9 @@ public class RequestMappingInfoHandlerMappingTests { RequestMappingInfo key = paths().build(); ServerWebExchange exchange = get("/1/2").toExchange(); String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value(); - this.handlerMapping.handleMatch(key, lookupPath, exchange); + this.handlerMapping.handleMatch(key, handlerMethod, lookupPath, exchange); - PathPattern bestMatch = (PathPattern) exchange.getAttributes() - .get(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); + PathPattern bestMatch = (PathPattern) exchange.getAttributes().get(BEST_MATCHING_PATTERN_ATTRIBUTE); assertEquals("/1/2", bestMatch.getPatternString()); } @@ -330,7 +338,6 @@ public class RequestMappingInfoHandlerMappingTests { .consumeErrorWith(error -> { assertEquals(exceptionClass, error.getClass()); consumer.accept((T) error); - }) .verify(); } @@ -375,19 +382,19 @@ public class RequestMappingInfoHandlerMappingTests { private void handleMatch(ServerWebExchange exchange, String pattern) { RequestMappingInfo info = paths(pattern).build(); String lookupPath = exchange.getRequest().getPath().pathWithinApplication().value(); - this.handlerMapping.handleMatch(info, lookupPath, exchange); + this.handlerMapping.handleMatch(info, handlerMethod, lookupPath, exchange); } @SuppressWarnings("unchecked") private MultiValueMap getMatrixVariables(ServerWebExchange exchange, String uriVarName) { - String attrName = HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE; - return ((Map>) exchange.getAttributes().get(attrName)).get(uriVarName); + return ((Map>) exchange.getAttributes() + .get(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE)).get(uriVarName); } @SuppressWarnings("unchecked") private Map getUriTemplateVariables(ServerWebExchange exchange) { - String attrName = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE; - return (Map) exchange.getAttributes().get(attrName); + return (Map) exchange.getAttributes() + .get(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); } @@ -446,6 +453,8 @@ public class RequestMappingInfoHandlerMappingTests { headers.add("Allow", "PUT,POST"); return headers; } + + public void dummy() { } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerMapping.java index 02ad6e7b74..4244506a35 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerMapping.java @@ -96,11 +96,11 @@ public interface HandlerMapping { /** * Name of the {@link HttpServletRequest} attribute that contains a map with - * URI matrix variables. + * URI variable names and a corresponding MultiValueMap of URI matrix + * variables for each. *

Note: This attribute is not required to be supported by all * HandlerMapping implementations and may also not be present depending on * whether the HandlerMapping is configured to keep matrix variable content - * in the request URI. */ String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";