Request attribute cache for resolved lookupPath

See gh-22644
This commit is contained in:
Rossen Stoyanchev 2019-04-03 12:03:33 -04:00
parent bb9fcad58a
commit 254f06e1a1
13 changed files with 69 additions and 18 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -91,6 +91,7 @@ public class PathExtensionContentNegotiationStrategy extends AbstractMappingCont
if (request == null) { if (request == null) {
return null; return null;
} }
// Ignore LOOKUP_PATH attribute, use our own "fixed" UrlPathHelper with decoding off
String path = this.urlPathHelper.getLookupPathForRequest(request); String path = this.urlPathHelper.getLookupPathForRequest(request);
String extension = UriUtils.extractFileExtension(path); String extension = UriUtils.extractFileExtension(path);
return (StringUtils.hasText(extension) ? extension.toLowerCase(Locale.ENGLISH) : null); return (StringUtils.hasText(extension) ? extension.toLowerCase(Locale.ENGLISH) : null);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2017 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -45,6 +45,9 @@ public class UrlBasedCorsConfigurationSource implements CorsConfigurationSource
private UrlPathHelper urlPathHelper = new UrlPathHelper(); private UrlPathHelper urlPathHelper = new UrlPathHelper();
@Nullable
private String lookupPathAttributeName;
/** /**
* Set the PathMatcher implementation to use for matching URL paths * Set the PathMatcher implementation to use for matching URL paths
@ -72,6 +75,17 @@ public class UrlBasedCorsConfigurationSource implements CorsConfigurationSource
this.urlPathHelper.setUrlDecode(urlDecode); this.urlPathHelper.setUrlDecode(urlDecode);
} }
/**
* Optionally configure the name of the attribute that caches the lookupPath.
* This is used to make the call to
* {@link UrlPathHelper#getLookupPathForRequest(HttpServletRequest, String)}
* @param lookupPathAttributeName the request attribute to check
* @since 5.2
*/
public void setLookupPathAttributeName(@Nullable String lookupPathAttributeName) {
this.lookupPathAttributeName = lookupPathAttributeName;
}
/** /**
* Shortcut to same property on underlying {@link #setUrlPathHelper UrlPathHelper}. * Shortcut to same property on underlying {@link #setUrlPathHelper UrlPathHelper}.
* @see org.springframework.web.util.UrlPathHelper#setRemoveSemicolonContent(boolean) * @see org.springframework.web.util.UrlPathHelper#setRemoveSemicolonContent(boolean)
@ -117,7 +131,7 @@ public class UrlBasedCorsConfigurationSource implements CorsConfigurationSource
@Override @Override
@Nullable @Nullable
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, this.lookupPathAttributeName);
for (Map.Entry<String, CorsConfiguration> entry : this.corsConfigurations.entrySet()) { for (Map.Entry<String, CorsConfiguration> entry : this.corsConfigurations.entrySet()) {
if (this.pathMatcher.match(entry.getKey(), lookupPath)) { if (this.pathMatcher.match(entry.getKey(), lookupPath)) {
return entry.getValue(); return entry.getValue();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -175,6 +175,26 @@ public class UrlPathHelper {
} }
} }
/**
* Variant of {@link #getLookupPathForRequest(HttpServletRequest)} that
* automates checking for a previously computed lookupPath saved as a
* request attribute. The attribute is only used for lookup purposes.
* @param request current HTTP request
* @param lookupPathAttributeName the request attribute to check
* @return the lookup path
* @since 5.2
* @see org.springframework.web.servlet.HandlerMapping#LOOKUP_PATH
*/
public String getLookupPathForRequest(HttpServletRequest request, @Nullable String lookupPathAttributeName) {
if (lookupPathAttributeName != null) {
String result = (String) request.getAttribute(lookupPathAttributeName);
if (result != null) {
return result;
}
}
return getLookupPathForRequest(request);
}
/** /**
* Return the path within the servlet mapping for the given request, * Return the path within the servlet mapping for the given request,
* i.e. the part of the request's URL beyond the part that called the servlet, * i.e. the part of the request's URL beyond the part that called the servlet,

View File

@ -62,6 +62,15 @@ public interface HandlerMapping {
*/ */
String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler"; String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";
/**
* Name of the {@link HttpServletRequest} attribute that contains the path
* used to look up the matching handler, which depending on the configured
* {@link org.springframework.web.util.UrlPathHelper} could be the full path
* or without the context path, decoded or not, etc.
* @since 5.2
*/
String LOOKUP_PATH = HandlerMapping.class.getName() + ".lookupPath";
/** /**
* Name of the {@link HttpServletRequest} attribute that contains the path * Name of the {@link HttpServletRequest} attribute that contains the path
* within the handler mapping, in case of a pattern match, or the full * within the handler mapping, in case of a pattern match, or the full

View File

@ -212,6 +212,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
source.setCorsConfigurations(corsConfigurations); source.setCorsConfigurations(corsConfigurations);
source.setPathMatcher(this.pathMatcher); source.setPathMatcher(this.pathMatcher);
source.setUrlPathHelper(this.urlPathHelper); source.setUrlPathHelper(this.urlPathHelper);
source.setLookupPathAttributeName(LOOKUP_PATH);
this.corsConfigurationSource = source; this.corsConfigurationSource = source;
} }
else { else {
@ -463,7 +464,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) { for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) { if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;

View File

@ -355,6 +355,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
@Override @Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
this.mappingRegistry.acquireReadLock(); this.mappingRegistry.acquireReadLock();
try { try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);

View File

@ -120,6 +120,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping i
@Nullable @Nullable
protected Object getHandlerInternal(HttpServletRequest request) throws Exception { protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
Object handler = lookupHandler(lookupPath, request); Object handler = lookupHandler(lookupPath, request);
if (handler == null) { if (handler == null) {
// We need to care for the default handler directly, since we need to // We need to care for the default handler directly, since we need to
@ -291,7 +292,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping i
@Override @Override
@Nullable @Nullable
public RequestMatchResult match(HttpServletRequest request, String pattern) { public RequestMatchResult match(HttpServletRequest request, String pattern) {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); String lookupPath = getUrlPathHelper().getLookupPathForRequest(request, LOOKUP_PATH);
if (getPathMatcher().match(pattern, lookupPath)) { if (getPathMatcher().match(pattern, lookupPath)) {
return new RequestMatchResult(pattern, lookupPath, getPathMatcher()); return new RequestMatchResult(pattern, lookupPath, getPathMatcher());
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -110,7 +110,7 @@ public class UrlFilenameViewController extends AbstractUrlViewController {
protected String extractOperableUrl(HttpServletRequest request) { protected String extractOperableUrl(HttpServletRequest request) {
String urlPath = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE); String urlPath = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
if (!StringUtils.hasText(urlPath)) { if (!StringUtils.hasText(urlPath)) {
urlPath = getUrlPathHelper().getLookupPathForRequest(request); urlPath = getUrlPathHelper().getLookupPathForRequest(request, HandlerMapping.LOOKUP_PATH);
} }
return urlPath; return urlPath;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -30,6 +30,7 @@ import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.PathMatcher; import org.springframework.util.PathMatcher;
import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.support.WebContentGenerator; import org.springframework.web.servlet.support.WebContentGenerator;
import org.springframework.web.util.UrlPathHelper; import org.springframework.web.util.UrlPathHelper;
@ -169,7 +170,7 @@ public class WebContentInterceptor extends WebContentGenerator implements Handle
checkRequest(request); checkRequest(request);
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, HandlerMapping.LOOKUP_PATH);
CacheControl cacheControl = lookupCacheControl(lookupPath); CacheControl cacheControl = lookupCacheControl(lookupPath);
Integer cacheSeconds = lookupCacheSeconds(lookupPath); Integer cacheSeconds = lookupCacheSeconds(lookupPath);

View File

@ -31,6 +31,7 @@ import org.springframework.lang.Nullable;
import org.springframework.util.AntPathMatcher; import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher; import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.util.UrlPathHelper; import org.springframework.web.util.UrlPathHelper;
/** /**
@ -212,7 +213,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
if (this.patterns.isEmpty()) { if (this.patterns.isEmpty()) {
return this; return this;
} }
String lookupPath = this.pathHelper.getLookupPathForRequest(request); String lookupPath = this.pathHelper.getLookupPathForRequest(request, HandlerMapping.LOOKUP_PATH);
List<String> matches = getMatchingPatterns(lookupPath); List<String> matches = getMatchingPatterns(lookupPath);
return !matches.isEmpty() ? new PatternsRequestCondition(new LinkedHashSet<>(matches), this) : null; return !matches.isEmpty() ? new PatternsRequestCondition(new LinkedHashSet<>(matches), this) : null;
} }
@ -287,7 +288,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
*/ */
@Override @Override
public int compareTo(PatternsRequestCondition other, HttpServletRequest request) { public int compareTo(PatternsRequestCondition other, HttpServletRequest request) {
String lookupPath = this.pathHelper.getLookupPathForRequest(request); String lookupPath = this.pathHelper.getLookupPathForRequest(request, HandlerMapping.LOOKUP_PATH);
Comparator<String> patternComparator = this.pathMatcher.getPatternComparator(lookupPath); Comparator<String> patternComparator = this.pathMatcher.getPatternComparator(lookupPath);
Iterator<String> iterator = this.patterns.iterator(); Iterator<String> iterator = this.patterns.iterator();
Iterator<String> iteratorOther = other.patterns.iterator(); Iterator<String> iteratorOther = other.patterns.iterator();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -340,7 +340,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
return null; return null;
} }
Set<String> patterns = matchingInfo.getPatternsCondition().getPatterns(); Set<String> patterns = matchingInfo.getPatternsCondition().getPatterns();
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); String lookupPath = getUrlPathHelper().getLookupPathForRequest(request, LOOKUP_PATH);
return new RequestMatchResult(patterns.iterator().next(), lookupPath, getPathMatcher()); return new RequestMatchResult(patterns.iterator().next(), lookupPath, getPathMatcher());
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -34,6 +34,7 @@ import org.springframework.lang.Nullable;
import org.springframework.util.AntPathMatcher; import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher; import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping; import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.util.UrlPathHelper; import org.springframework.web.util.UrlPathHelper;
@ -180,7 +181,7 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
private int getLookupPathIndex(HttpServletRequest request) { private int getLookupPathIndex(HttpServletRequest request) {
UrlPathHelper pathHelper = getUrlPathHelper(); UrlPathHelper pathHelper = getUrlPathHelper();
String requestUri = pathHelper.getRequestUri(request); String requestUri = pathHelper.getRequestUri(request);
String lookupPath = pathHelper.getLookupPathForRequest(request); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, HandlerMapping.LOOKUP_PATH);
return requestUri.indexOf(lookupPath); return requestUri.indexOf(lookupPath);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2017 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,6 +21,7 @@ import javax.servlet.http.HttpServletRequest;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.RequestToViewNameTranslator; import org.springframework.web.servlet.RequestToViewNameTranslator;
import org.springframework.web.util.UrlPathHelper; import org.springframework.web.util.UrlPathHelper;
@ -167,7 +168,7 @@ public class DefaultRequestToViewNameTranslator implements RequestToViewNameTran
*/ */
@Override @Override
public String getViewName(HttpServletRequest request) { public String getViewName(HttpServletRequest request) {
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, HandlerMapping.LOOKUP_PATH);
return (this.prefix + transformPath(lookupPath) + this.suffix); return (this.prefix + transformPath(lookupPath) + this.suffix);
} }