Skip to content

Commit 378c72e

Browse files
committed
Polish + minor refactoring of ResourceUrlProvider
1 parent 118d147 commit 378c72e

File tree

4 files changed

+60
-103
lines changed

4 files changed

+60
-103
lines changed

spring-web/src/main/java/org/springframework/http/server/reactive/ServerHttpResponse.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public interface ServerHttpResponse extends ReactiveHttpOutputMessage {
6464
* HTML template libraries to use consistently for all URLs emitted by
6565
* the application. Doing so enables the registration of URL encoders via
6666
* {@link #registerUrlEncoder} that can insert an id for authentication,
67-
* a nonce for CSRF protection, a version for a static resource, etc.
67+
* a nonce for CSRF protection, or other.
6868
* @param url the URL to encode
6969
* @return the encoded URL or the same
7070
*/

spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceTransformerSupport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ protected Mono<String> resolveUrlPath(String resourcePath, ServerWebExchange exc
7676
if (resourcePath.startsWith("/")) {
7777
// full resource path
7878
ResourceUrlProvider urlProvider = getResourceUrlProvider();
79-
return (urlProvider != null ? urlProvider.getForRequestUrl(exchange, resourcePath) : Mono.empty());
79+
return (urlProvider != null ? urlProvider.getForUriString(resourcePath, exchange) : Mono.empty());
8080
}
8181
else {
8282
// try resolving as relative path

spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceUrlProvider.java

Lines changed: 46 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@
3333
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
3434
import org.springframework.http.server.reactive.PathContainer;
3535
import org.springframework.http.server.reactive.ServerHttpRequest;
36-
import org.springframework.lang.Nullable;
37-
import org.springframework.util.Assert;
3836
import org.springframework.util.StringUtils;
3937
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
4038
import org.springframework.web.server.ServerWebExchange;
@@ -57,50 +55,49 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
5755
private static final Log logger = LogFactory.getLog(ResourceUrlProvider.class);
5856

5957

60-
private final PathPatternParser patternParser;
58+
private final PathPatternParser patternParser = new PathPatternParser();
6159

6260
private final Map<PathPattern, ResourceWebHandler> handlerMap = new LinkedHashMap<>();
6361

64-
private boolean autodetect = true;
65-
66-
67-
public ResourceUrlProvider() {
68-
this(new PathPatternParser());
69-
}
70-
71-
public ResourceUrlProvider(PathPatternParser patternParser) {
72-
Assert.notNull(patternParser, "'patternParser' is required.");
73-
this.patternParser = patternParser;
74-
}
75-
7662

7763
/**
7864
* Return a read-only view of the resource handler mappings either manually
79-
* configured or auto-detected when the Spring {@code ApplicationContext}
80-
* is refreshed.
65+
* configured or auto-detected from Spring configuration.
8166
*/
8267
public Map<PathPattern, ResourceWebHandler> getHandlerMap() {
8368
return Collections.unmodifiableMap(this.handlerMap);
8469
}
8570

71+
8672
/**
87-
* Return {@code false} if resource mappings were manually configured,
88-
* {@code true} otherwise.
73+
* Manually configure resource handler mappings.
74+
* <p><strong>Note:</strong> by default resource mappings are auto-detected
75+
* from the Spring {@code ApplicationContext}. If this property is used,
76+
* auto-detection is turned off.
8977
*/
90-
public boolean isAutodetect() {
91-
return this.autodetect;
78+
public void registerHandlers(Map<String, ResourceWebHandler> handlerMap) {
79+
this.handlerMap.clear();
80+
handlerMap.forEach((rawPattern, resourceWebHandler) -> {
81+
rawPattern = prependLeadingSlash(rawPattern);
82+
PathPattern pattern = this.patternParser.parse(rawPattern);
83+
this.handlerMap.put(pattern, resourceWebHandler);
84+
});
9285
}
9386

87+
private static String prependLeadingSlash(String pattern) {
88+
if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {
89+
return "/" + pattern;
90+
}
91+
else {
92+
return pattern;
93+
}
94+
}
9495

9596
@Override
9697
public void onApplicationEvent(ContextRefreshedEvent event) {
97-
if (isAutodetect()) {
98-
this.handlerMap.clear();
98+
if (this.handlerMap.isEmpty()) {
9999
detectResourceHandlers(event.getApplicationContext());
100-
if (!this.handlerMap.isEmpty()) {
101-
this.autodetect = false;
102-
}
103-
else if(logger.isDebugEnabled()) {
100+
if(logger.isDebugEnabled()) {
104101
logger.debug("No resource handling mappings found");
105102
}
106103
}
@@ -110,10 +107,10 @@ private void detectResourceHandlers(ApplicationContext context) {
110107
logger.debug("Looking for resource handler mappings");
111108

112109
Map<String, SimpleUrlHandlerMapping> map = context.getBeansOfType(SimpleUrlHandlerMapping.class);
113-
List<SimpleUrlHandlerMapping> handlerMappings = new ArrayList<>(map.values());
114-
AnnotationAwareOrderComparator.sort(handlerMappings);
110+
List<SimpleUrlHandlerMapping> mappings = new ArrayList<>(map.values());
111+
AnnotationAwareOrderComparator.sort(mappings);
115112

116-
handlerMappings.forEach(mapping -> {
113+
mappings.forEach(mapping -> {
117114
mapping.getHandlerMap().forEach((pattern, handler) -> {
118115
if (handler instanceof ResourceWebHandler) {
119116
ResourceWebHandler resourceHandler = (ResourceWebHandler) handler;
@@ -128,85 +125,45 @@ private void detectResourceHandlers(ApplicationContext context) {
128125
});
129126
}
130127

131-
/**
132-
* Manually configure the resource mappings.
133-
* <p><strong>Note:</strong> by default resource mappings are auto-detected
134-
* from the Spring {@code ApplicationContext}. However if this property is
135-
* used, the auto-detection is turned off.
136-
*/
137-
public void registerHandlers(@Nullable Map<String, ResourceWebHandler> handlerMap) {
138-
if (handlerMap == null) {
139-
return;
140-
}
141-
this.handlerMap.clear();
142-
handlerMap.forEach((rawPattern, resourceWebHandler) -> {
143-
rawPattern = prependLeadingSlash(rawPattern);
144-
PathPattern pattern = this.patternParser.parse(rawPattern);
145-
this.handlerMap.put(pattern, resourceWebHandler);
146-
});
147-
this.autodetect = false;
148-
}
149-
150-
private static String prependLeadingSlash(String pattern) {
151-
if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {
152-
return "/" + pattern;
153-
}
154-
else {
155-
return pattern;
156-
}
157-
}
158-
159128

160129
/**
161-
* A variation on {@link #getForLookupPath(PathContainer)} that accepts a
162-
* full request URL path and returns the full request URL path to expose
163-
* for public use.
130+
* Get the public resource URL for the given URI string.
131+
* <p>The URI string is expected to be a path and if it contains a query or
132+
* fragment those will be preserved in the resulting public resource URL.
133+
* @param uriString the URI string to transform
164134
* @param exchange the current exchange
165-
* @param requestUrl the request URL path to resolve
166-
* @return the resolved public URL path, or empty if unresolved
135+
* @return the resolved public resource URL path, or empty if unresolved
167136
*/
168-
public final Mono<String> getForRequestUrl(ServerWebExchange exchange, String requestUrl) {
137+
public final Mono<String> getForUriString(String uriString, ServerWebExchange exchange) {
169138
if (logger.isTraceEnabled()) {
170-
logger.trace("Getting resource URL for request URL \"" + requestUrl + "\"");
139+
logger.trace("Getting resource URL for request URL \"" + uriString + "\"");
171140
}
172141
ServerHttpRequest request = exchange.getRequest();
173-
int queryIndex = getQueryIndex(requestUrl);
174-
String lookupPath = requestUrl.substring(0, queryIndex);
175-
String query = requestUrl.substring(queryIndex);
142+
int queryIndex = getQueryIndex(uriString);
143+
String lookupPath = uriString.substring(0, queryIndex);
144+
String query = uriString.substring(queryIndex);
176145
PathContainer parsedLookupPath = PathContainer.parseUrlPath(lookupPath);
177-
return getForLookupPath(parsedLookupPath).map(resolvedPath ->
146+
if (logger.isTraceEnabled()) {
147+
logger.trace("Getting resource URL for lookup path \"" + lookupPath + "\"");
148+
}
149+
return resolveResourceUrl(parsedLookupPath).map(resolvedPath ->
178150
request.getPath().contextPath().value() + resolvedPath + query);
179151
}
180152

181-
private int getQueryIndex(String lookupPath) {
182-
int suffixIndex = lookupPath.length();
183-
int queryIndex = lookupPath.indexOf("?");
153+
private int getQueryIndex(String path) {
154+
int suffixIndex = path.length();
155+
int queryIndex = path.indexOf("?");
184156
if (queryIndex > 0) {
185157
suffixIndex = queryIndex;
186158
}
187-
int hashIndex = lookupPath.indexOf("#");
159+
int hashIndex = path.indexOf("#");
188160
if (hashIndex > 0) {
189161
suffixIndex = Math.min(suffixIndex, hashIndex);
190162
}
191163
return suffixIndex;
192164
}
193165

194-
/**
195-
* Compare the given path against configured resource handler mappings and
196-
* if a match is found use the {@code ResourceResolver} chain of the matched
197-
* {@code ResourceHttpRequestHandler} to resolve the URL path to expose for
198-
* public use.
199-
* <p>It is expected that the given path is what Spring uses for
200-
* request mapping purposes.
201-
* <p>If several handler mappings match, the handler used will be the one
202-
* configured with the most specific pattern.
203-
* @param lookupPath the lookup path to check
204-
* @return the resolved public URL path, or empty if unresolved
205-
*/
206-
public final Mono<String> getForLookupPath(PathContainer lookupPath) {
207-
if (logger.isTraceEnabled()) {
208-
logger.trace("Getting resource URL for lookup path \"" + lookupPath + "\"");
209-
}
166+
private Mono<String> resolveResourceUrl(PathContainer lookupPath) {
210167
return this.handlerMap.entrySet().stream()
211168
.filter(entry -> entry.getKey().matches(lookupPath))
212169
.sorted(Comparator.comparing(Map.Entry::getKey))

spring-webflux/src/test/java/org/springframework/web/reactive/resource/ResourceUrlProviderTests.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import org.springframework.context.annotation.Configuration;
3333
import org.springframework.core.io.ClassPathResource;
3434
import org.springframework.core.io.Resource;
35-
import org.springframework.http.server.reactive.PathContainer;
3635
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
3736
import org.springframework.mock.web.test.MockServletContext;
3837
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
@@ -41,7 +40,6 @@
4140
import org.springframework.web.util.pattern.PathPattern;
4241

4342
import static org.junit.Assert.assertEquals;
44-
import static org.junit.Assert.assertFalse;
4543
import static org.junit.Assert.assertThat;
4644

4745

@@ -74,21 +72,22 @@ public void setup() throws Exception {
7472

7573
@Test
7674
public void getStaticResourceUrl() {
77-
PathContainer path = PathContainer.parsePath("/resources/foo.css");
78-
String url = this.urlProvider.getForLookupPath(path).block(Duration.ofSeconds(5));
79-
assertEquals("/resources/foo.css", url);
75+
ServerWebExchange exchange = MockServerHttpRequest.get("/").toExchange();
76+
String uriString = "/resources/foo.css";
77+
String actual = this.urlProvider.getForUriString(uriString, exchange).block(Duration.ofSeconds(5));
78+
assertEquals(uriString, actual);
8079
}
8180

8281
@Test // SPR-13374
8382
public void getStaticResourceUrlRequestWithQueryOrHash() {
8483
ServerWebExchange exchange = MockServerHttpRequest.get("/").toExchange();
8584

8685
String url = "/resources/foo.css?foo=bar&url=http://example.org";
87-
String resolvedUrl = this.urlProvider.getForRequestUrl(exchange, url).block(Duration.ofSeconds(5));
86+
String resolvedUrl = this.urlProvider.getForUriString(url, exchange).block(Duration.ofSeconds(5));
8887
assertEquals(url, resolvedUrl);
8988

9089
url = "/resources/foo.css#hash";
91-
resolvedUrl = this.urlProvider.getForRequestUrl(exchange, url).block(Duration.ofSeconds(5));
90+
resolvedUrl = this.urlProvider.getForUriString(url, exchange).block(Duration.ofSeconds(5));
9291
assertEquals(url, resolvedUrl);
9392
}
9493

@@ -104,8 +103,9 @@ public void getFingerprintedResourceUrl() {
104103
resolvers.add(new PathResourceResolver());
105104
this.handler.setResourceResolvers(resolvers);
106105

107-
PathContainer path = PathContainer.parsePath("/resources/foo.css");
108-
String url = this.urlProvider.getForLookupPath(path).block(Duration.ofSeconds(5));
106+
ServerWebExchange exchange = MockServerHttpRequest.get("/").toExchange();
107+
String path = "/resources/foo.css";
108+
String url = this.urlProvider.getForUriString(path, exchange).block(Duration.ofSeconds(5));
109109
assertEquals("/resources/foo-e36d2e05253c6c7085a91522ce43a0b4.css", url);
110110
}
111111

@@ -126,8 +126,9 @@ public void bestPatternMatch() throws Exception {
126126
this.handlerMap.put("/resources/*.css", otherHandler);
127127
this.urlProvider.registerHandlers(this.handlerMap);
128128

129-
PathContainer path = PathContainer.parsePath("/resources/foo.css");
130-
String url = this.urlProvider.getForLookupPath(path).block(Duration.ofSeconds(5));
129+
ServerWebExchange exchange = MockServerHttpRequest.get("/").toExchange();
130+
String path = "/resources/foo.css";
131+
String url = this.urlProvider.getForUriString(path, exchange).block(Duration.ofSeconds(5));
131132
assertEquals("/resources/foo-e36d2e05253c6c7085a91522ce43a0b4.css", url);
132133
}
133134

@@ -140,7 +141,6 @@ public void initializeOnce() throws Exception {
140141

141142
ResourceUrlProvider urlProviderBean = context.getBean(ResourceUrlProvider.class);
142143
assertThat(urlProviderBean.getHandlerMap(), Matchers.hasKey(pattern("/resources/**")));
143-
assertFalse(urlProviderBean.isAutodetect());
144144
}
145145

146146

0 commit comments

Comments
 (0)