@Test public void shouldConfigureFixedPrefixAutomatically() throws Exception { this.resolver.addFixedVersionStrategy("fixedversion", "/js/**", "/css/**", "/fixedversion/css/**"); Matcher<VersionStrategy> matcher = Matchers.instanceOf(FixedVersionStrategy.class); assertThat(this.resolver.getStrategyMap().size(), is(4)); assertThat(this.resolver.getStrategyForPath("js/something.js"), matcher); assertThat(this.resolver.getStrategyForPath("fixedversion/js/something.js"), matcher); assertThat(this.resolver.getStrategyForPath("css/something.css"), matcher); assertThat(this.resolver.getStrategyForPath("fixedversion/css/something.css"), matcher); }
@Before public void setup() { VersionResourceResolver versionResourceResolver = new VersionResourceResolver(); versionResourceResolver.setStrategyMap(Collections.singletonMap("/**", this.versionStrategy)); }
/** * Insert a content-based version in resource URLs that match the given path * patterns. The version is computed from the content of the file, e.g. * {@code "css/main-e36d2e05253c6c7085a91522ce43a0b4.css"}. This is a good * default strategy to use except when it cannot be, for example when using * JavaScript module loaders, use {@link #addFixedVersionStrategy} instead * for serving JavaScript files. * @param pathPatterns one or more resource URL path patterns, * relative to the pattern configured with the resource handler * @return the current instance for chained method invocation * @see ContentVersionStrategy */ public VersionResourceResolver addContentVersionStrategy(String... pathPatterns) { addVersionStrategy(new ContentVersionStrategy(), pathPatterns); return this; }
@Test public void getStrategyForPath() throws Exception { Map<String, VersionStrategy> strategies = new HashMap<>(); VersionStrategy jsStrategy = mock(VersionStrategy.class); VersionStrategy catchAllStrategy = mock(VersionStrategy.class); strategies.put("/**", catchAllStrategy); strategies.put("/**/*.js", jsStrategy); this.resolver.setStrategyMap(strategies); assertEquals(catchAllStrategy, this.resolver.getStrategyForPath("foo.css")); assertEquals(catchAllStrategy, this.resolver.getStrategyForPath("foo-js.css")); assertEquals(jsStrategy, this.resolver.getStrategyForPath("foo.js")); assertEquals(jsStrategy, this.resolver.getStrategyForPath("bar/foo.js")); }
@Override protected String resolveUrlPathInternal(String resourceUrlPath, List<? extends Resource> locations, ResourceResolverChain chain) { String baseUrl = chain.resolveUrlPath(resourceUrlPath, locations); if (StringUtils.hasText(baseUrl)) { VersionStrategy versionStrategy = getStrategyForPath(resourceUrlPath); if (versionStrategy == null) { return baseUrl; } Resource resource = chain.resolveResource(null, baseUrl, locations); Assert.state(resource != null, "Unresolvable resource"); String version = versionStrategy.getResourceVersion(resource); return versionStrategy.addVersion(baseUrl, version); } return baseUrl; }
/** * Register a custom VersionStrategy to apply to resource URLs that match the * given path patterns. * @param strategy the custom strategy * @param pathPatterns one or more resource URL path patterns, * relative to the pattern configured with the resource handler * @return the current instance for chained method invocation * @see VersionStrategy */ public VersionResourceResolver addVersionStrategy(VersionStrategy strategy, String... pathPatterns) { for (String pattern : pathPatterns) { getStrategyMap().put(pattern, strategy); } return this; }
@Test public void resolveResourceNoVersionStrategy() throws Exception { String file = "missing.css"; given(this.chain.resolveResource(null, file, this.locations)).willReturn(null); this.resolver.setStrategyMap(Collections.emptyMap()); Resource actual = this.resolver.resolveResourceInternal(null, file, this.locations, this.chain); assertNull(actual); verify(this.chain, times(1)).resolveResource(null, file, this.locations); }
@Test public void resourceChainWithVersionResolver() throws Exception { VersionResourceResolver versionResolver = new VersionResourceResolver() .addFixedVersionStrategy("fixed", "/**/*.js") .addContentVersionStrategy("/**"); this.registration.resourceChain(true).addResolver(versionResolver) .addTransformer(new AppCacheManifestTransformer()); ResourceHttpRequestHandler handler = getHandler("/resources/**"); List<ResourceResolver> resolvers = handler.getResourceResolvers(); assertThat(resolvers.toString(), resolvers, Matchers.hasSize(4)); assertThat(resolvers.get(0), Matchers.instanceOf(CachingResourceResolver.class)); assertThat(resolvers.get(1), Matchers.sameInstance(versionResolver)); assertThat(resolvers.get(2), Matchers.instanceOf(WebJarsResourceResolver.class)); assertThat(resolvers.get(3), Matchers.instanceOf(PathResourceResolver.class)); List<ResourceTransformer> transformers = handler.getResourceTransformers(); assertThat(transformers, Matchers.hasSize(3)); assertThat(transformers.get(0), Matchers.instanceOf(CachingResourceTransformer.class)); assertThat(transformers.get(1), Matchers.instanceOf(CssLinkResourceTransformer.class)); assertThat(transformers.get(2), Matchers.instanceOf(AppCacheManifestTransformer.class)); }
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**") .addResourceLocations("classpath:org/springframework/web/servlet/resource/test/") .resourceChain(true).addResolver(new VersionResourceResolver().addContentVersionStrategy("/**")); } }
@Test public void getVersionedResource() throws Exception { VersionResourceResolver versionResolver = new VersionResourceResolver() .addFixedVersionStrategy("versionString", "/**"); this.handler.setResourceResolvers(Arrays.asList(versionResolver, new PathResourceResolver())); this.handler.afterPropertiesSet(); this.request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "versionString/foo.css"); this.handler.handleRequest(this.request, this.response); assertEquals("\"versionString\"", this.response.getHeader("ETag")); assertEquals("bytes", this.response.getHeader("Accept-Ranges")); assertEquals(1, this.response.getHeaders("Accept-Ranges").size()); }
@Before public void setup() { this.locations = new ArrayList<>(); this.locations.add(new ClassPathResource("test/", getClass())); this.locations.add(new ClassPathResource("testalternatepath/", getClass())); this.resolver = new VersionResourceResolver(); this.chain = mock(ResourceResolverChain.class); this.versionStrategy = mock(VersionStrategy.class); }
@Override protected String resolveUrlPathInternal(String resourceUrlPath, List<? extends Resource> locations, ResourceResolverChain chain) { if (resourceVersioningEnabled && !bundlingService.checkForRegisteredBundleFile(resourceUrlPath)) { String result = super.resolveUrlPathInternal(resourceUrlPath, locations, chain); // Spring's default version handler will return null if it doesn't have a strategy // for that resource - that seems incorrect. Overriding here. if (result == null) { return chain.resolveUrlPath(resourceUrlPath, locations); } else { return result; } } else { return chain.resolveUrlPath(resourceUrlPath, locations); } }
@Override protected Resource resolveResourceInternal(HttpServletRequest request, String requestPath, List<? extends Resource> locations, ResourceResolverChain chain) { if (resourceVersioningEnabled && !bundlingService.checkForRegisteredBundleFile(requestPath)) { return super.resolveResourceInternal(request, requestPath, locations, chain); } else { return chain.resolveResource(request, requestPath, locations); } }
VersionStrategy versionStrategy = getStrategyForPath(requestPath); if (versionStrategy == null) { return null;
/** * Register a custom VersionStrategy to apply to resource URLs that match the * given path patterns. * @param strategy the custom strategy * @param pathPatterns one or more resource URL path patterns, * relative to the pattern configured with the resource handler * @return the current instance for chained method invocation * @see VersionStrategy */ public VersionResourceResolver addVersionStrategy(VersionStrategy strategy, String... pathPatterns) { for (String pattern : pathPatterns) { getStrategyMap().put(pattern, strategy); } return this; }
@Test public void resolveResourceExisting() throws Exception { String file = "bar.css"; Resource expected = new ClassPathResource("test/" + file, getClass()); given(this.chain.resolveResource(null, file, this.locations)).willReturn(expected); this.resolver.setStrategyMap(Collections.singletonMap("/**", this.versionStrategy)); Resource actual = this.resolver.resolveResourceInternal(null, file, this.locations, this.chain); assertEquals(expected, actual); verify(this.chain, times(1)).resolveResource(null, file, this.locations); verify(this.versionStrategy, never()).extractVersion(file); }
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**") .addResourceLocations("classpath:static/") .resourceChain(false) .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**")); }
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**") .addResourceLocations("/**") .resourceChain(true) .addResolver( new VersionResourceResolver().addContentVersionStrategy("/**")); }
@Test // SPR-15372 public void resolveUrlPathNoVersionStrategy() throws Exception { given(this.chain.resolveUrlPath("/foo.css", this.locations)).willReturn("/foo.css"); String resolved = this.resolver.resolveUrlPathInternal("/foo.css", this.locations, this.chain); assertThat(resolved, is("/foo.css")); }
@Before public void createFilter() { VersionResourceResolver versionResolver = new VersionResourceResolver(); versionResolver.setStrategyMap(Collections.singletonMap("/**", new ContentVersionStrategy())); PathResourceResolver pathResolver = new PathResourceResolver(); pathResolver.setAllowedLocations(new ClassPathResource("test/", getClass())); List<ResourceResolver> resolvers = new ArrayList<>(); resolvers.add(versionResolver); resolvers.add(pathResolver); this.filter = new ResourceUrlEncodingFilter(); this.urlProvider = createResourceUrlProvider(resolvers); }