private Mono<Resource> resolveVersionedResource(@Nullable ServerWebExchange exchange, String requestPath, List<? extends Resource> locations, ResourceResolverChain chain) { VersionStrategy versionStrategy = getStrategyForPath(requestPath); if (versionStrategy == null) { return Mono.empty(); } String candidate = versionStrategy.extractVersion(requestPath); if (StringUtils.isEmpty(candidate)) { return Mono.empty(); } String simplePath = versionStrategy.removeVersion(requestPath, candidate); return chain.resolveResource(exchange, simplePath, locations) .filterWhen(resource -> versionStrategy.getResourceVersion(resource) .map(actual -> { if (candidate.equals(actual)) { return true; } else { if (logger.isTraceEnabled()) { String logPrefix = exchange != null ? exchange.getLogPrefix() : ""; logger.trace(logPrefix + "Found resource for \"" + requestPath + "\", but version [" + candidate + "] does not match"); } return false; } })) .map(resource -> new FileNameVersionedResource(resource, candidate)); }
@Override protected Mono<String> resolveUrlPathInternal(String resourceUrlPath, List<? extends Resource> locations, ResourceResolverChain chain) { return chain.resolveUrlPath(resourceUrlPath, locations) .flatMap(baseUrl -> { if (StringUtils.hasText(baseUrl)) { VersionStrategy strategy = getStrategyForPath(resourceUrlPath); if (strategy == null) { return Mono.just(baseUrl); } return chain.resolveResource(null, baseUrl, locations) .flatMap(resource -> strategy.getResourceVersion(resource) .map(version -> strategy.addVersion(baseUrl, version))); } return Mono.empty(); }); }
@Test public void resolveResourceNoResourceAfterVersionRemoved() { String versionFile = "bar-version.css"; String version = "version"; String file = "bar.css"; given(this.chain.resolveResource(null, versionFile, this.locations)).willReturn(Mono.empty()); given(this.chain.resolveResource(null, file, this.locations)).willReturn(Mono.empty()); given(this.versionStrategy.extractVersion(versionFile)).willReturn(version); given(this.versionStrategy.removeVersion(versionFile, version)).willReturn(file); this.resolver.setStrategyMap(Collections.singletonMap("/**", this.versionStrategy)); Resource actual = this.resolver .resolveResourceInternal(null, versionFile, this.locations, this.chain) .block(Duration.ofMillis(5000)); assertNull(actual); verify(this.versionStrategy, times(1)).removeVersion(versionFile, version); }
@Test public void resolveResourceExisting() { String file = "bar.css"; Resource expected = new ClassPathResource("test/" + file, getClass()); given(this.chain.resolveResource(null, file, this.locations)).willReturn(Mono.just(expected)); this.resolver.setStrategyMap(Collections.singletonMap("/**", this.versionStrategy)); Resource actual = this.resolver .resolveResourceInternal(null, file, this.locations, this.chain) .block(Duration.ofMillis(5000)); assertEquals(expected, actual); verify(this.chain, times(1)).resolveResource(null, file, this.locations); verify(this.versionStrategy, never()).extractVersion(file); }
@Test public void resolveResourceNoVersionInPath() { String file = "bar.css"; given(this.chain.resolveResource(null, file, this.locations)).willReturn(Mono.empty()); given(this.versionStrategy.extractVersion(file)).willReturn(""); this.resolver.setStrategyMap(Collections.singletonMap("/**", this.versionStrategy)); Resource actual = this.resolver .resolveResourceInternal(null, file, this.locations, this.chain) .block(Duration.ofMillis(5000)); assertNull(actual); verify(this.chain, times(1)).resolveResource(null, file, this.locations); verify(this.versionStrategy, times(1)).extractVersion(file); }
@Test public void resolveResourceVersionDoesNotMatch() { String versionFile = "bar-version.css"; String version = "version"; String file = "bar.css"; Resource expected = new ClassPathResource("test/" + file, getClass()); given(this.chain.resolveResource(null, versionFile, this.locations)).willReturn(Mono.empty()); given(this.chain.resolveResource(null, file, this.locations)).willReturn(Mono.just(expected)); given(this.versionStrategy.extractVersion(versionFile)).willReturn(version); given(this.versionStrategy.removeVersion(versionFile, version)).willReturn(file); given(this.versionStrategy.getResourceVersion(expected)).willReturn(Mono.just("newer-version")); this.resolver.setStrategyMap(Collections.singletonMap("/**", this.versionStrategy)); Resource actual = this.resolver .resolveResourceInternal(null, versionFile, this.locations, this.chain) .block(Duration.ofMillis(5000)); assertNull(actual); verify(this.versionStrategy, times(1)).getResourceVersion(expected); }
@Override protected Mono<String> resolveUrlPathInternal(String resourceUrlPath, List<? extends Resource> locations, ResourceResolverChain chain) { return chain.resolveUrlPath(resourceUrlPath, locations) .then(baseUrl -> { if (StringUtils.hasText(baseUrl)) { VersionStrategy versionStrategy = getStrategyForPath(resourceUrlPath); if (versionStrategy == null) { return Mono.empty(); } if (logger.isTraceEnabled()) { logger.trace("Getting the original resource to determine version " + "for path \"" + resourceUrlPath + "\""); } return chain.resolveResource(null, baseUrl, locations) .map(resource -> { String version = versionStrategy.getResourceVersion(resource); if (logger.isTraceEnabled()) { logger.trace("Determined version [" + version + "] for " + resource); } return versionStrategy.addVersion(baseUrl, version); }); } return Mono.empty(); }); }
@Test public void resolveResourceSuccess() { String versionFile = "bar-version.css"; String version = "version"; String file = "bar.css"; Resource expected = new ClassPathResource("test/" + file, getClass()); MockServerHttpRequest request = MockServerHttpRequest.get("/resources/bar-version.css").build(); ServerWebExchange exchange = MockServerWebExchange.from(request); given(this.chain.resolveResource(exchange, versionFile, this.locations)).willReturn(Mono.empty()); given(this.chain.resolveResource(exchange, file, this.locations)).willReturn(Mono.just(expected)); given(this.versionStrategy.extractVersion(versionFile)).willReturn(version); given(this.versionStrategy.removeVersion(versionFile, version)).willReturn(file); given(this.versionStrategy.getResourceVersion(expected)).willReturn(Mono.just(version)); this.resolver.setStrategyMap(Collections.singletonMap("/**", this.versionStrategy)); Resource actual = this.resolver .resolveResourceInternal(exchange, versionFile, this.locations, this.chain) .block(Duration.ofMillis(5000)); assertEquals(expected.getFilename(), actual.getFilename()); verify(this.versionStrategy, times(1)).getResourceVersion(expected); assertThat(actual, instanceOf(HttpResource.class)); assertEquals("\"" + version + "\"", ((HttpResource)actual).getResponseHeaders().getETag()); }
String candidateVersion = versionStrategy.extractVersion(requestPath); if (StringUtils.isEmpty(candidateVersion)) { if (logger.isTraceEnabled()) { String simplePath = versionStrategy.removeVersion(requestPath, candidateVersion); if (logger.isTraceEnabled()) { logger.trace("Extracted version from path, re-resolving without version: \"" + simplePath + "\""); String actualVersion = versionStrategy.getResourceVersion(baseResource); if (candidateVersion.equals(actualVersion)) { if (logger.isTraceEnabled()) {