Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ public interface HandlerMapping {
*/
String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";

/**
* Name of the {@link HttpServletRequest} attribute that contains the best
* matching {@link org.springframework.web.util.pattern.PathPattern}, when
* parsed patterns are in use.
* @since 7.1
*/
String BEST_MATCHING_PATH_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPathPattern";

/**
* Name of the boolean {@link HttpServletRequest} attribute that indicates
* whether type-level mappings should be inspected.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ private void setAttributes(HttpServletRequest servletRequest, ServerRequest requ
if (matchingPattern != null) {
servletRequest.removeAttribute(RouterFunctions.MATCHING_PATTERN_ATTRIBUTE);
servletRequest.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, matchingPattern.getPatternString());
servletRequest.setAttribute(BEST_MATCHING_PATH_PATTERN_ATTRIBUTE, matchingPattern);
ServerHttpObservationFilter.findObservationContext(request.servletRequest())
.ifPresent(context -> context.setPathPattern(matchingPattern.getPatternString()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ private String getHandlerDescription(Object handler) {
pathWithinMapping = UrlPathHelper.defaultInstance.removeSemicolonContent(pathWithinMapping);
PathPattern.PathMatchInfo pathMatchInfo = pattern.matchAndExtract(path);
Map<String, String> uriVariables = (pathMatchInfo != null ? pathMatchInfo.getUriVariables(): null);
return buildPathExposingHandler(handler, pattern.getPatternString(), pathWithinMapping, uriVariables);
return buildPathExposingHandler(handler, pattern, pathWithinMapping, uriVariables);
}

/**
Expand Down Expand Up @@ -419,12 +419,14 @@ protected void validateHandler(Object handler, HttpServletRequest request) throw

/**
* Build a handler object for the given raw handler, exposing the actual
* handler, the {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}, as well as
* handler, the {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE},
* the {@link #BEST_MATCHING_PATTERN_ATTRIBUTE}, as well as
* the {@link #URI_TEMPLATE_VARIABLES_ATTRIBUTE} before executing the handler.
* <p>The default implementation builds a {@link HandlerExecutionChain}
* with a special interceptor that exposes the path attribute and URI
* template variables
* @param rawHandler the raw handler to expose
* @param bestMatchingPattern the {@linkplain HandlerMapping#BEST_MATCHING_PATTERN_ATTRIBUTE best matching pattern}
* @param pathWithinMapping the path to expose before executing the handler
* @param uriTemplateVariables the URI template variables, can be {@code null} if no variables found
* @return the final handler object
Expand All @@ -440,8 +442,33 @@ protected Object buildPathExposingHandler(Object rawHandler, String bestMatching
return chain;
}

/**
* Build a handler object for the given raw handler, exposing the actual
* handler, the {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE},
* the {@link #BEST_MATCHING_PATH_PATTERN_ATTRIBUTE}
* the {@link #BEST_MATCHING_PATTERN_ATTRIBUTE}, as well as
* the {@link #URI_TEMPLATE_VARIABLES_ATTRIBUTE} before executing the handler.
* @param rawHandler the raw handler to expose
* @param bestMatchingPattern the {@linkplain HandlerMapping#BEST_MATCHING_PATH_PATTERN_ATTRIBUTE best matching pattern}
* @param pathWithinMapping the path to expose before executing the handler
* @param uriTemplateVariables the URI template variables, can be {@code null} if no variables found
* @return the final handler object
* @since 7.1
*/
protected Object buildPathExposingHandler(Object rawHandler, PathPattern bestMatchingPattern,
String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) {

HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
}
return chain;
}

/**
* Expose the path within the current mapping as request attribute.
* @param bestMatchingPattern the best matching pattern
* @param pathWithinMapping the path within the current mapping
* @param request the request to expose the path to
* @see #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE
Expand All @@ -455,6 +482,22 @@ protected void exposePathWithinMapping(String bestMatchingPattern, String pathWi
request.setAttribute(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);
}

/**
* Expose the path within the current mapping as request attribute.
* @param bestMatchingPattern the best matching pattern
* @param pathWithinMapping the path within the current mapping
* @param request the request to expose the path to
* @since 7.1
* @see #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE
* @see #BEST_MATCHING_PATH_PATTERN_ATTRIBUTE
*/
protected void exposePathWithinMapping(PathPattern bestMatchingPattern, String pathWithinMapping,
HttpServletRequest request) {

request.setAttribute(BEST_MATCHING_PATH_PATTERN_ATTRIBUTE, bestMatchingPattern);
exposePathWithinMapping(bestMatchingPattern.getPatternString(), pathWithinMapping, request);
}

/**
* Expose the URI templates variables as request attribute.
* @param uriTemplateVariables the URI template variables
Expand Down Expand Up @@ -495,23 +538,39 @@ protected boolean supportsTypeLevelMappings() {

/**
* Special interceptor for exposing the
* {@link AbstractUrlHandlerMapping#PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE} attribute.
* {@link AbstractUrlHandlerMapping#PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE} and,
* when parsed patterns are in use, the
* {@link AbstractUrlHandlerMapping#BEST_MATCHING_PATH_PATTERN_ATTRIBUTE} attribute.
* @see AbstractUrlHandlerMapping#exposePathWithinMapping
*/
private class PathExposingHandlerInterceptor implements HandlerInterceptor {

private final String bestMatchingPattern;

private final @Nullable PathPattern bestMatchingPathPattern;

private final String pathWithinMapping;

public PathExposingHandlerInterceptor(String bestMatchingPattern, String pathWithinMapping) {
this.bestMatchingPattern = bestMatchingPattern;
this.bestMatchingPathPattern = null;
this.pathWithinMapping = pathWithinMapping;
}

public PathExposingHandlerInterceptor(PathPattern bestMatchingPattern, String pathWithinMapping) {
this.bestMatchingPattern = bestMatchingPattern.getPatternString();
this.bestMatchingPathPattern = bestMatchingPattern;
this.pathWithinMapping = pathWithinMapping;
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
exposePathWithinMapping(this.bestMatchingPattern, this.pathWithinMapping, request);
if (this.bestMatchingPathPattern != null) {
exposePathWithinMapping(this.bestMatchingPathPattern, this.pathWithinMapping, request);
}
else {
exposePathWithinMapping(this.bestMatchingPattern, this.pathWithinMapping, request);
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, handler);
request.setAttribute(INTROSPECT_TYPE_LEVEL_MAPPING, supportsTypeLevelMappings());
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ private void extractMatchDetails(
uriVariables = result.getUriVariables();
request.setAttribute(MATRIX_VARIABLES_ATTRIBUTE, result.getMatrixVariables());
}
request.setAttribute(BEST_MATCHING_PATH_PATTERN_ATTRIBUTE, bestPattern);
request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern.getPatternString());
ServerHttpObservationFilter.findObservationContext(request)
.ifPresent(context -> context.setPathPattern(bestPattern.getPatternString()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
import org.springframework.web.util.ServletRequestPathUtils;
import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPatternParser;

import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -174,6 +175,9 @@ void mappedRequestShouldHoldAttributes() throws Exception {

assertThat(result).isNotNull();
assertThat(request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)).isEqualTo("/match");
assertThat(request.getAttribute(HandlerMapping.BEST_MATCHING_PATH_PATTERN_ATTRIBUTE))
.isInstanceOfSatisfying(PathPattern.class,
pattern -> assertThat(pattern.getPatternString()).isEqualTo("/match"));
assertThat(ServerHttpObservationFilter.findObservationContext(request))
.hasValueSatisfying(context -> assertThat(context.getPathPattern()).isEqualTo("/match"));
assertThat(request.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE)).isEqualTo(handlerFunction);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,15 @@
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.web.util.WebUtils;
import org.springframework.web.util.pattern.PathPattern;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Named.named;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import static org.springframework.web.servlet.HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE;
import static org.springframework.web.servlet.HandlerMapping.BEST_MATCHING_PATH_PATTERN_ATTRIBUTE;
import static org.springframework.web.servlet.HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE;
import static org.springframework.web.servlet.HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE;
import static org.springframework.web.servlet.HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;

Expand Down Expand Up @@ -99,6 +102,30 @@ void resolveFromMap(SimpleUrlHandlerMapping handlerMapping) throws Exception {
assertThat(request.getAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE)).isEqualTo(mainController);
}

@HandlerMappingsTest
void resolveBestMatchingPathPatternAttribute(SimpleUrlHandlerMapping handlerMapping) throws Exception {
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("mainController", Object.class);
Object mainController = applicationContext.getBean("mainController");
handlerMapping.setUrlMap(Map.of("/welcome*", "mainController"));
handlerMapping.setApplicationContext(applicationContext);

boolean usePathPatterns = handlerMapping.getPatternParser() != null;
MockHttpServletRequest request = PathPatternsTestUtils.initRequest("GET", "/welcome.x", usePathPatterns);
HandlerExecutionChain chain = getHandler(handlerMapping, request);

assertThat(chain.getHandler()).isSameAs(mainController);
assertThat(request.getAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE)).isEqualTo("/welcome*");
if (usePathPatterns) {
assertThat(request.getAttribute(BEST_MATCHING_PATH_PATTERN_ATTRIBUTE))
.isInstanceOfSatisfying(PathPattern.class,
pattern -> assertThat(pattern.getPatternString()).isEqualTo("/welcome*"));
}
else {
assertThat(request.getAttribute(BEST_MATCHING_PATH_PATTERN_ATTRIBUTE)).isNull();
}
}

@HandlerMappingsTest
void resolvePatternFromMap(SimpleUrlHandlerMapping handlerMapping) throws Exception {
StaticApplicationContext applicationContext = new StaticApplicationContext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
import org.springframework.web.util.ServletRequestPathUtils;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.web.util.pattern.PathPattern;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
Expand Down Expand Up @@ -315,6 +316,22 @@ void handleMatchBestMatchingPatternAttribute(TestRequestMappingInfoHandlerMappin
mapping.handleMatch(info, "/1/2", request);

assertThat(request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)).isEqualTo("/{path1}/2");
assertThat(request.getAttribute(HandlerMapping.BEST_MATCHING_PATH_PATTERN_ATTRIBUTE)).isNull();
}

@Test
void handleMatchBestMatchingPathPatternAttribute() {
TestRequestMappingInfoHandlerMapping mapping = new TestRequestMappingInfoHandlerMapping();
RequestMappingInfo info = mapping.createInfo("/{path1}/2", "/**");
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/1/2");
ServletRequestPathUtils.parseAndCache(request);

mapping.handleMatch(info, "/1/2", request);

assertThat(request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)).isEqualTo("/{path1}/2");
assertThat(request.getAttribute(HandlerMapping.BEST_MATCHING_PATH_PATTERN_ATTRIBUTE))
.isInstanceOfSatisfying(PathPattern.class,
pattern -> assertThat(pattern.getPatternString()).isEqualTo("/{path1}/2"));
}

@SuppressWarnings("removal")
Expand Down
Loading