Resource handling configuration
Issue: SPR-14521
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.reactive.resource.CachingResourceResolver;
|
||||
import org.springframework.web.reactive.resource.CachingResourceTransformer;
|
||||
import org.springframework.web.reactive.resource.CssLinkResourceTransformer;
|
||||
import org.springframework.web.reactive.resource.PathResourceResolver;
|
||||
import org.springframework.web.reactive.resource.ResourceResolver;
|
||||
import org.springframework.web.reactive.resource.ResourceTransformer;
|
||||
import org.springframework.web.reactive.resource.VersionResourceResolver;
|
||||
import org.springframework.web.reactive.resource.WebJarsResourceResolver;
|
||||
|
||||
/**
|
||||
* Assists with the registration of resource resolvers and transformers.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ResourceChainRegistration {
|
||||
|
||||
private static final String DEFAULT_CACHE_NAME = "spring-resource-chain-cache";
|
||||
|
||||
private static final boolean isWebJarsAssetLocatorPresent = ClassUtils.isPresent(
|
||||
"org.webjars.WebJarAssetLocator", ResourceChainRegistration.class.getClassLoader());
|
||||
|
||||
|
||||
private final List<ResourceResolver> resolvers = new ArrayList<>(4);
|
||||
|
||||
private final List<ResourceTransformer> transformers = new ArrayList<>(4);
|
||||
|
||||
private boolean hasVersionResolver;
|
||||
|
||||
private boolean hasPathResolver;
|
||||
|
||||
private boolean hasCssLinkTransformer;
|
||||
|
||||
private boolean hasWebjarsResolver;
|
||||
|
||||
|
||||
public ResourceChainRegistration(boolean cacheResources) {
|
||||
this(cacheResources, cacheResources ? new ConcurrentMapCache(DEFAULT_CACHE_NAME) : null);
|
||||
}
|
||||
|
||||
public ResourceChainRegistration(boolean cacheResources, Cache cache) {
|
||||
Assert.isTrue(!cacheResources || cache != null, "'cache' is required when cacheResources=true");
|
||||
if (cacheResources) {
|
||||
this.resolvers.add(new CachingResourceResolver(cache));
|
||||
this.transformers.add(new CachingResourceTransformer(cache));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a resource resolver to the chain.
|
||||
* @param resolver the resolver to add
|
||||
* @return the current instance for chained method invocation
|
||||
*/
|
||||
public ResourceChainRegistration addResolver(ResourceResolver resolver) {
|
||||
Assert.notNull(resolver, "The provided ResourceResolver should not be null");
|
||||
this.resolvers.add(resolver);
|
||||
if (resolver instanceof VersionResourceResolver) {
|
||||
this.hasVersionResolver = true;
|
||||
}
|
||||
else if (resolver instanceof PathResourceResolver) {
|
||||
this.hasPathResolver = true;
|
||||
}
|
||||
else if (resolver instanceof WebJarsResourceResolver) {
|
||||
this.hasWebjarsResolver = true;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a resource transformer to the chain.
|
||||
* @param transformer the transformer to add
|
||||
* @return the current instance for chained method invocation
|
||||
*/
|
||||
public ResourceChainRegistration addTransformer(ResourceTransformer transformer) {
|
||||
Assert.notNull(transformer, "The provided ResourceTransformer should not be null");
|
||||
this.transformers.add(transformer);
|
||||
if (transformer instanceof CssLinkResourceTransformer) {
|
||||
this.hasCssLinkTransformer = true;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
protected List<ResourceResolver> getResourceResolvers() {
|
||||
if (!this.hasPathResolver) {
|
||||
List<ResourceResolver> result = new ArrayList<>(this.resolvers);
|
||||
if (isWebJarsAssetLocatorPresent && !this.hasWebjarsResolver) {
|
||||
result.add(new WebJarsResourceResolver());
|
||||
}
|
||||
result.add(new PathResourceResolver());
|
||||
return result;
|
||||
}
|
||||
return this.resolvers;
|
||||
}
|
||||
|
||||
protected List<ResourceTransformer> getResourceTransformers() {
|
||||
if (this.hasVersionResolver && !this.hasCssLinkTransformer) {
|
||||
List<ResourceTransformer> result = new ArrayList<>(this.transformers);
|
||||
boolean hasTransformers = !this.transformers.isEmpty();
|
||||
boolean hasCaching = hasTransformers && this.transformers.get(0) instanceof CachingResourceTransformer;
|
||||
result.add(hasCaching ? 1 : 0, new CssLinkResourceTransformer());
|
||||
return result;
|
||||
}
|
||||
return this.transformers;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.resource.ResourceWebHandler;
|
||||
|
||||
/**
|
||||
* Assist with creating and configuring a static resources handler.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ResourceHandlerRegistration {
|
||||
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
private final String[] pathPatterns;
|
||||
|
||||
private final List<Resource> locations = new ArrayList<>();
|
||||
|
||||
private CacheControl cacheControl;
|
||||
|
||||
private ResourceChainRegistration resourceChainRegistration;
|
||||
|
||||
|
||||
/**
|
||||
* Create a {@link ResourceHandlerRegistration} instance.
|
||||
* @param resourceLoader a resource loader for turning a String location
|
||||
* into a {@link Resource}
|
||||
* @param pathPatterns one or more resource URL path patterns
|
||||
*/
|
||||
public ResourceHandlerRegistration(ResourceLoader resourceLoader, String... pathPatterns) {
|
||||
Assert.notEmpty(pathPatterns, "At least one path pattern is required for resource handling.");
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.pathPatterns = pathPatterns;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add one or more resource locations from which to serve static content.
|
||||
* Each location must point to a valid directory. Multiple locations may
|
||||
* be specified as a comma-separated list, and the locations will be checked
|
||||
* for a given resource in the order specified.
|
||||
*
|
||||
* <p>For example, {{@code "/"},
|
||||
* {@code "classpath:/META-INF/public-web-resources/"}} allows resources to
|
||||
* be served both from the web application root and from any JAR on the
|
||||
* classpath that contains a {@code /META-INF/public-web-resources/} directory,
|
||||
* with resources in the web application root taking precedence.
|
||||
* @return the same {@link ResourceHandlerRegistration} instance, for
|
||||
* chained method invocation
|
||||
*/
|
||||
public ResourceHandlerRegistration addResourceLocations(String... resourceLocations) {
|
||||
for (String location : resourceLocations) {
|
||||
this.locations.add(resourceLoader.getResource(location));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the {@link CacheControl} which should be used
|
||||
* by the resource handler.
|
||||
*
|
||||
* @param cacheControl the CacheControl configuration to use
|
||||
* @return the same {@link ResourceHandlerRegistration} instance, for
|
||||
* chained method invocation
|
||||
*/
|
||||
public ResourceHandlerRegistration setCacheControl(CacheControl cacheControl) {
|
||||
this.cacheControl = cacheControl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a chain of resource resolvers and transformers to use. This
|
||||
* can be useful, for example, to apply a version strategy to resource URLs.
|
||||
*
|
||||
* <p>If this method is not invoked, by default only a simple
|
||||
* {@code PathResourceResolver} is used in order to match URL paths to
|
||||
* resources under the configured locations.
|
||||
*
|
||||
* @param cacheResources whether to cache the result of resource resolution;
|
||||
* setting this to "true" is recommended for production (and "false" for
|
||||
* development, especially when applying a version strategy)
|
||||
* @return the same {@link ResourceHandlerRegistration} instance, for
|
||||
* chained method invocation
|
||||
*/
|
||||
public ResourceChainRegistration resourceChain(boolean cacheResources) {
|
||||
this.resourceChainRegistration = new ResourceChainRegistration(cacheResources);
|
||||
return this.resourceChainRegistration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a chain of resource resolvers and transformers to use. This
|
||||
* can be useful, for example, to apply a version strategy to resource URLs.
|
||||
*
|
||||
* <p>If this method is not invoked, by default only a simple
|
||||
* {@code PathResourceResolver} is used in order to match URL paths to
|
||||
* resources under the configured locations.
|
||||
*
|
||||
* @param cacheResources whether to cache the result of resource resolution;
|
||||
* setting this to "true" is recommended for production (and "false" for
|
||||
* development, especially when applying a version strategy
|
||||
* @param cache the cache to use for storing resolved and transformed resources;
|
||||
* by default a {@link org.springframework.cache.concurrent.ConcurrentMapCache}
|
||||
* is used. Since Resources aren't serializable and can be dependent on the
|
||||
* application host, one should not use a distributed cache but rather an
|
||||
* in-memory cache.
|
||||
* @return the same {@link ResourceHandlerRegistration} instance, for chained method invocation
|
||||
*/
|
||||
public ResourceChainRegistration resourceChain(boolean cacheResources, Cache cache) {
|
||||
this.resourceChainRegistration = new ResourceChainRegistration(cacheResources, cache);
|
||||
return this.resourceChainRegistration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL path patterns for the resource handler.
|
||||
*/
|
||||
protected String[] getPathPatterns() {
|
||||
return this.pathPatterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link ResourceWebHandler} instance.
|
||||
*/
|
||||
protected ResourceWebHandler getRequestHandler() {
|
||||
ResourceWebHandler handler = new ResourceWebHandler();
|
||||
if (this.resourceChainRegistration != null) {
|
||||
handler.setResourceResolvers(this.resourceChainRegistration.getResourceResolvers());
|
||||
handler.setResourceTransformers(this.resourceChainRegistration.getResourceTransformers());
|
||||
}
|
||||
handler.setLocations(this.locations);
|
||||
if (this.cacheControl != null) {
|
||||
handler.setCacheControl(this.cacheControl);
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.accept.CompositeContentTypeResolver;
|
||||
import org.springframework.web.reactive.handler.AbstractHandlerMapping;
|
||||
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.reactive.resource.ResourceWebHandler;
|
||||
import org.springframework.web.server.WebHandler;
|
||||
|
||||
/**
|
||||
* Stores registrations of resource handlers for serving static resources such
|
||||
* as images, css files and others through Spring MVC including setting cache
|
||||
* headers optimized for efficient loading in a web browser. Resources can be
|
||||
* served out of locations under web application root, from the classpath, and
|
||||
* others.
|
||||
*
|
||||
* <p>To create a resource handler, use {@link #addResourceHandler(String...)}
|
||||
* providing the URL path patterns for which the handler should be invoked to
|
||||
* serve static resources (e.g. {@code "/resources/**"}).
|
||||
*
|
||||
* <p>Then use additional methods on the returned
|
||||
* {@link ResourceHandlerRegistration} to add one or more locations from which
|
||||
* to serve static content from (e.g. {{@code "/"},
|
||||
* {@code "classpath:/META-INF/public-web-resources/"}}) or to specify a cache
|
||||
* period for served resources.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ResourceHandlerRegistry {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
private final CompositeContentTypeResolver contentTypeResolver;
|
||||
|
||||
private final List<ResourceHandlerRegistration> registrations = new ArrayList<>();
|
||||
|
||||
private int order = Integer.MAX_VALUE -1;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new resource handler registry for the given application context.
|
||||
* @param applicationContext the Spring application context
|
||||
*/
|
||||
public ResourceHandlerRegistry(ApplicationContext applicationContext) {
|
||||
this(applicationContext, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new resource handler registry for the given application context.
|
||||
* @param applicationContext the Spring application context
|
||||
* @param contentTypeResolver the content type resolver to use
|
||||
*/
|
||||
public ResourceHandlerRegistry(ApplicationContext applicationContext,
|
||||
CompositeContentTypeResolver contentTypeResolver) {
|
||||
|
||||
Assert.notNull(applicationContext, "ApplicationContext is required");
|
||||
this.applicationContext = applicationContext;
|
||||
this.contentTypeResolver = contentTypeResolver;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a resource handler for serving static resources based on the specified
|
||||
* URL path patterns. The handler will be invoked for every incoming request
|
||||
* that matches to one of the specified path patterns.
|
||||
*
|
||||
* <p>Patterns like {@code "/static/**"} or {@code "/css/{filename:\\w+\\.css}"}
|
||||
* are allowed. See {@link org.springframework.util.AntPathMatcher} for more
|
||||
* details on the syntax.
|
||||
* @return A {@link ResourceHandlerRegistration} to use to further
|
||||
* configure the registered resource handler
|
||||
*/
|
||||
public ResourceHandlerRegistration addResourceHandler(String... patterns) {
|
||||
ResourceHandlerRegistration registration =
|
||||
new ResourceHandlerRegistration(this.applicationContext, patterns);
|
||||
this.registrations.add(registration);
|
||||
return registration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a resource handler has already been registered for the given path pattern.
|
||||
*/
|
||||
public boolean hasMappingForPattern(String pathPattern) {
|
||||
for (ResourceHandlerRegistration registration : this.registrations) {
|
||||
if (Arrays.asList(registration.getPathPatterns()).contains(pathPattern)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the order to use for resource handling relative to other
|
||||
* {@code HandlerMapping}s configured in the Spring configuration.
|
||||
* <p>The default value used is {@code Integer.MAX_VALUE-1}.
|
||||
*/
|
||||
public ResourceHandlerRegistry setOrder(int order) {
|
||||
this.order = order;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a handler mapping with the mapped resource handlers; or {@code null} in case
|
||||
* of no registrations.
|
||||
*/
|
||||
protected AbstractHandlerMapping getHandlerMapping() {
|
||||
if (this.registrations.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, WebHandler> urlMap = new LinkedHashMap<>();
|
||||
for (ResourceHandlerRegistration registration : this.registrations) {
|
||||
for (String pathPattern : registration.getPathPatterns()) {
|
||||
ResourceWebHandler handler = registration.getRequestHandler();
|
||||
handler.setContentTypeResolver(this.contentTypeResolver);
|
||||
try {
|
||||
handler.afterPropertiesSet();
|
||||
handler.afterSingletonsInstantiated();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new BeanInitializationException("Failed to init ResourceHttpRequestHandler", ex);
|
||||
}
|
||||
urlMap.put(pathPattern, handler);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
|
||||
handlerMapping.setOrder(order);
|
||||
handlerMapping.setUrlMap(urlMap);
|
||||
return handlerMapping;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,6 +21,8 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -52,8 +54,10 @@ import org.springframework.http.codec.xml.Jaxb2XmlEncoder;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
import org.springframework.web.reactive.accept.CompositeContentTypeResolver;
|
||||
import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder;
|
||||
import org.springframework.web.reactive.handler.AbstractHandlerMapping;
|
||||
import org.springframework.web.reactive.result.SimpleHandlerAdapter;
|
||||
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter;
|
||||
@@ -62,6 +66,7 @@ import org.springframework.web.reactive.result.method.annotation.ResponseBodyRes
|
||||
import org.springframework.web.reactive.result.method.annotation.ResponseEntityResultHandler;
|
||||
import org.springframework.web.reactive.result.view.ViewResolutionResultHandler;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* The main class for Spring Web Reactive configuration.
|
||||
@@ -135,7 +140,7 @@ public class WebReactiveConfiguration implements ApplicationContextAware {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RequestedContentTypeResolver mvcContentTypeResolver() {
|
||||
public CompositeContentTypeResolver mvcContentTypeResolver() {
|
||||
RequestedContentTypeResolverBuilder builder = new RequestedContentTypeResolverBuilder();
|
||||
builder.mediaTypes(getDefaultMediaTypeMappings());
|
||||
configureRequestedContentTypeResolver(builder);
|
||||
@@ -178,6 +183,39 @@ public class WebReactiveConfiguration implements ApplicationContextAware {
|
||||
public void configurePathMatching(PathMatchConfigurer configurer) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped
|
||||
* resource handlers. To configure resource handling, override
|
||||
* {@link #addResourceHandlers}.
|
||||
*/
|
||||
@Bean
|
||||
public HandlerMapping resourceHandlerMapping() {
|
||||
ResourceHandlerRegistry registry =
|
||||
new ResourceHandlerRegistry(this.applicationContext, mvcContentTypeResolver());
|
||||
addResourceHandlers(registry);
|
||||
|
||||
AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
|
||||
if (handlerMapping != null) {
|
||||
if (getPathMatchConfigurer() != null) {
|
||||
handlerMapping.setPathMatcher(getPathMatchConfigurer().getPathMatcher());
|
||||
}
|
||||
if (getPathMatchConfigurer() != null) {
|
||||
handlerMapping.setPathHelper(getPathMatchConfigurer().getPathHelper());
|
||||
}
|
||||
}
|
||||
else {
|
||||
handlerMapping = new EmptyHandlerMapping();
|
||||
}
|
||||
return handlerMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to add resource handlers for serving static resources.
|
||||
* @see ResourceHandlerRegistry
|
||||
*/
|
||||
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
|
||||
RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
|
||||
@@ -397,6 +435,14 @@ public class WebReactiveConfiguration implements ApplicationContextAware {
|
||||
}
|
||||
|
||||
|
||||
private static final class EmptyHandlerMapping extends AbstractHandlerMapping {
|
||||
|
||||
@Override
|
||||
public Mono<Object> getHandler(ServerWebExchange exchange) {
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class NoOpValidator implements Validator {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.config;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse;
|
||||
import org.springframework.tests.TestSubscriber;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.reactive.resource.AppCacheManifestTransformer;
|
||||
import org.springframework.web.reactive.resource.CachingResourceResolver;
|
||||
import org.springframework.web.reactive.resource.CachingResourceTransformer;
|
||||
import org.springframework.web.reactive.resource.CssLinkResourceTransformer;
|
||||
import org.springframework.web.reactive.resource.PathResourceResolver;
|
||||
import org.springframework.web.reactive.resource.ResourceResolver;
|
||||
import org.springframework.web.reactive.resource.ResourceTransformer;
|
||||
import org.springframework.web.reactive.resource.ResourceWebHandler;
|
||||
import org.springframework.web.reactive.resource.VersionResourceResolver;
|
||||
import org.springframework.web.reactive.resource.WebJarsResourceResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.DefaultWebSessionManager;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ResourceHandlerRegistry}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ResourceHandlerRegistryTests {
|
||||
|
||||
private ResourceHandlerRegistry registry;
|
||||
|
||||
private ResourceHandlerRegistration registration;
|
||||
|
||||
private ServerWebExchange exchange;
|
||||
|
||||
private MockServerHttpResponse response;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.registry = new ResourceHandlerRegistry(new GenericApplicationContext());
|
||||
this.registration = this.registry.addResourceHandler("/resources/**");
|
||||
this.registration.addResourceLocations("classpath:org/springframework/web/reactive/config/");
|
||||
|
||||
MockServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, "");
|
||||
this.response = new MockServerHttpResponse();
|
||||
WebSessionManager manager = new DefaultWebSessionManager();
|
||||
this.exchange = new DefaultServerWebExchange(request, this.response, manager);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void noResourceHandlers() throws Exception {
|
||||
this.registry = new ResourceHandlerRegistry(new GenericApplicationContext());
|
||||
assertNull(this.registry.getHandlerMapping());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapPathToLocation() throws Exception {
|
||||
this.exchange.getAttributes().put(
|
||||
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "/testStylesheet.css");
|
||||
|
||||
ResourceWebHandler handler = getHandler("/resources/**");
|
||||
handler.handle(this.exchange).blockMillis(5000);
|
||||
|
||||
TestSubscriber.subscribe(this.response.getBody())
|
||||
.assertValuesWith(buf -> assertEquals("test stylesheet content",
|
||||
DataBufferTestUtils.dumpString(buf, StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheControl() {
|
||||
assertThat(getHandler("/resources/**").getCacheControl(),
|
||||
Matchers.nullValue());
|
||||
|
||||
this.registration.setCacheControl(CacheControl.noCache().cachePrivate());
|
||||
assertThat(getHandler("/resources/**").getCacheControl().getHeaderValue(),
|
||||
Matchers.equalTo(CacheControl.noCache().cachePrivate().getHeaderValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void order() {
|
||||
assertEquals(Integer.MAX_VALUE -1, this.registry.getHandlerMapping().getOrder());
|
||||
|
||||
this.registry.setOrder(0);
|
||||
assertEquals(0, this.registry.getHandlerMapping().getOrder());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasMappingForPattern() {
|
||||
assertTrue(this.registry.hasMappingForPattern("/resources/**"));
|
||||
assertFalse(this.registry.hasMappingForPattern("/whatever"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resourceChain() throws Exception {
|
||||
ResourceResolver mockResolver = Mockito.mock(ResourceResolver.class);
|
||||
ResourceTransformer mockTransformer = Mockito.mock(ResourceTransformer.class);
|
||||
this.registration.resourceChain(true).addResolver(mockResolver).addTransformer(mockTransformer);
|
||||
|
||||
ResourceWebHandler handler = getHandler("/resources/**");
|
||||
List<ResourceResolver> resolvers = handler.getResourceResolvers();
|
||||
assertThat(resolvers.toString(), resolvers, Matchers.hasSize(4));
|
||||
assertThat(resolvers.get(0), Matchers.instanceOf(CachingResourceResolver.class));
|
||||
CachingResourceResolver cachingResolver = (CachingResourceResolver) resolvers.get(0);
|
||||
assertThat(cachingResolver.getCache(), Matchers.instanceOf(ConcurrentMapCache.class));
|
||||
assertThat(resolvers.get(1), Matchers.equalTo(mockResolver));
|
||||
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(2));
|
||||
assertThat(transformers.get(0), Matchers.instanceOf(CachingResourceTransformer.class));
|
||||
assertThat(transformers.get(1), Matchers.equalTo(mockTransformer));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resourceChainWithoutCaching() throws Exception {
|
||||
this.registration.resourceChain(false);
|
||||
|
||||
ResourceWebHandler handler = getHandler("/resources/**");
|
||||
List<ResourceResolver> resolvers = handler.getResourceResolvers();
|
||||
assertThat(resolvers, Matchers.hasSize(2));
|
||||
assertThat(resolvers.get(0), Matchers.instanceOf(WebJarsResourceResolver.class));
|
||||
assertThat(resolvers.get(1), Matchers.instanceOf(PathResourceResolver.class));
|
||||
|
||||
List<ResourceTransformer> transformers = handler.getResourceTransformers();
|
||||
assertThat(transformers, Matchers.hasSize(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resourceChainWithVersionResolver() throws Exception {
|
||||
VersionResourceResolver versionResolver = new VersionResourceResolver()
|
||||
.addFixedVersionStrategy("fixed", "/**/*.js")
|
||||
.addContentVersionStrategy("/**");
|
||||
|
||||
this.registration.resourceChain(true).addResolver(versionResolver)
|
||||
.addTransformer(new AppCacheManifestTransformer());
|
||||
|
||||
ResourceWebHandler 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));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resourceChainWithOverrides() throws Exception {
|
||||
CachingResourceResolver cachingResolver = Mockito.mock(CachingResourceResolver.class);
|
||||
VersionResourceResolver versionResolver = Mockito.mock(VersionResourceResolver.class);
|
||||
WebJarsResourceResolver webjarsResolver = Mockito.mock(WebJarsResourceResolver.class);
|
||||
PathResourceResolver pathResourceResolver = new PathResourceResolver();
|
||||
CachingResourceTransformer cachingTransformer = Mockito.mock(CachingResourceTransformer.class);
|
||||
AppCacheManifestTransformer appCacheTransformer = Mockito.mock(AppCacheManifestTransformer.class);
|
||||
CssLinkResourceTransformer cssLinkTransformer = new CssLinkResourceTransformer();
|
||||
|
||||
this.registration.setCacheControl(CacheControl.maxAge(3600, TimeUnit.MILLISECONDS))
|
||||
.resourceChain(false)
|
||||
.addResolver(cachingResolver)
|
||||
.addResolver(versionResolver)
|
||||
.addResolver(webjarsResolver)
|
||||
.addResolver(pathResourceResolver)
|
||||
.addTransformer(cachingTransformer)
|
||||
.addTransformer(appCacheTransformer)
|
||||
.addTransformer(cssLinkTransformer);
|
||||
|
||||
ResourceWebHandler handler = getHandler("/resources/**");
|
||||
List<ResourceResolver> resolvers = handler.getResourceResolvers();
|
||||
assertThat(resolvers.toString(), resolvers, Matchers.hasSize(4));
|
||||
assertThat(resolvers.get(0), Matchers.sameInstance(cachingResolver));
|
||||
assertThat(resolvers.get(1), Matchers.sameInstance(versionResolver));
|
||||
assertThat(resolvers.get(2), Matchers.sameInstance(webjarsResolver));
|
||||
assertThat(resolvers.get(3), Matchers.sameInstance(pathResourceResolver));
|
||||
|
||||
List<ResourceTransformer> transformers = handler.getResourceTransformers();
|
||||
assertThat(transformers, Matchers.hasSize(3));
|
||||
assertThat(transformers.get(0), Matchers.sameInstance(cachingTransformer));
|
||||
assertThat(transformers.get(1), Matchers.sameInstance(appCacheTransformer));
|
||||
assertThat(transformers.get(2), Matchers.sameInstance(cssLinkTransformer));
|
||||
}
|
||||
|
||||
private ResourceWebHandler getHandler(String pathPattern) {
|
||||
SimpleUrlHandlerMapping mapping = (SimpleUrlHandlerMapping) this.registry.getHandlerMapping();
|
||||
return (ResourceWebHandler) mapping.getUrlMap().get(pathPattern);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
test stylesheet content
|
||||
Reference in New Issue
Block a user