Streamlined WebContentGenerator API variants: checkRequest, prepareResponse, applyCacheControl, applyCacheSeconds

Issue: SPR-11792
This commit is contained in:
Juergen Hoeller 2015-07-21 20:33:03 +02:00
parent b277c081e7
commit 7c22d60fd8
10 changed files with 258 additions and 230 deletions

View File

@ -25,7 +25,7 @@ import org.springframework.web.servlet.support.WebContentGenerator;
import org.springframework.web.util.WebUtils;
/**
* <p>Convenient superclass for controller implementations, using the Template Method
* Convenient superclass for controller implementations, using the Template Method
* design pattern.
*
* <p><b><a name="workflow">Workflow
@ -130,7 +130,8 @@ public abstract class AbstractController extends WebContentGenerator implements
throws Exception {
// Delegate to WebContentGenerator for checking and preparing.
checkAndPrepare(request, response);
checkRequest(request);
prepareResponse(response);
// Execute handleRequestInternal in synchronized block if required.
if (this.synchronizeOnSession) {
@ -152,6 +153,6 @@ public abstract class AbstractController extends WebContentGenerator implements
* @see #handleRequest
*/
protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception;
throws Exception;
}

View File

@ -35,13 +35,14 @@ import org.springframework.web.servlet.support.WebContentGenerator;
import org.springframework.web.util.UrlPathHelper;
/**
* Interceptor that checks and prepares request and response. Checks for supported
* methods and a required session, and applies the specified {@link org.springframework.http.CacheControl}.
* Handler interceptor that checks the request and prepares the response.
* Checks for supported methods and a required session, and applies the
* specified {@link org.springframework.http.CacheControl} builder.
* See superclass bean properties for configuration options.
*
* <p>All the settings supported by this interceptor can also be set on AbstractController.
* This interceptor is mainly intended for applying checks and preparations to a set of
* controllers mapped by a HandlerMapping.
* <p>All the settings supported by this interceptor can also be set on
* {@link AbstractController}. This interceptor is mainly intended for applying
* checks and preparations to a set of controllers mapped by a HandlerMapping.
*
* @author Juergen Hoeller
* @author Brian Clozel
@ -58,9 +59,10 @@ public class WebContentInterceptor extends WebContentGenerator implements Handle
private Map<String, CacheControl> cacheControlMappings = new HashMap<String, CacheControl>();
public WebContentInterceptor() {
// no restriction of HTTP methods by default,
// in particular for use with annotated controllers
// No restriction of HTTP methods by default,
// in particular for use with annotated controllers...
super(false);
}
@ -134,11 +136,9 @@ public class WebContentInterceptor extends WebContentGenerator implements Handle
* <p>Overrides the default cache seconds setting of this interceptor.
* Can specify a empty {@link org.springframework.http.CacheControl} instance
* to exclude a URL path from default caching.
*
* <p>Supports direct matches, e.g. a registered "/test" matches "/test",
* and a various Ant-style pattern matches, e.g. a registered "/t*" matches
* both "/test" and "/team". For details, see the AntPathMatcher javadoc.
*
* @param cacheControl the {@code CacheControl} to use
* @param paths URL paths that will map to the given {@code CacheControl}
* @see #setCacheSeconds
@ -151,7 +151,6 @@ public class WebContentInterceptor extends WebContentGenerator implements Handle
}
}
/**
* Set the PathMatcher implementation to use for matching URL paths
* against registered URL patterns, for determining cache mappings.
@ -168,7 +167,9 @@ public class WebContentInterceptor extends WebContentGenerator implements Handle
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws ServletException {
throws ServletException {
checkRequest(request);
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
@ -181,19 +182,19 @@ public class WebContentInterceptor extends WebContentGenerator implements Handle
if (logger.isDebugEnabled()) {
logger.debug("Applying CacheControl to [" + lookupPath + "]");
}
checkAndPrepare(request, response, cacheControl);
applyCacheControl(response, cacheControl);
}
else if (cacheSeconds != null) {
if (logger.isDebugEnabled()) {
logger.debug("Applying CacheControl to [" + lookupPath + "]");
}
checkAndPrepare(request, response, cacheSeconds);
applyCacheSeconds(response, cacheSeconds);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Applying default cache seconds to [" + lookupPath + "]");
}
checkAndPrepare(request, response);
prepareResponse(response);
}
return true;
@ -209,10 +210,10 @@ public class WebContentInterceptor extends WebContentGenerator implements Handle
* @see org.springframework.util.AntPathMatcher
*/
protected CacheControl lookupCacheControl(String urlPath) {
// direct match?
// Direct match?
CacheControl cacheControl = this.cacheControlMappings.get(urlPath);
if (cacheControl == null) {
// pattern match?
// Pattern match?
for (String registeredPath : this.cacheControlMappings.keySet()) {
if (this.pathMatcher.match(registeredPath, urlPath)) {
cacheControl = this.cacheControlMappings.get(registeredPath);
@ -232,10 +233,10 @@ public class WebContentInterceptor extends WebContentGenerator implements Handle
* @see org.springframework.util.AntPathMatcher
*/
protected Integer lookupCacheSeconds(String urlPath) {
// direct match?
// Direct match?
Integer cacheSeconds = this.cacheMappings.get(urlPath);
if (cacheSeconds == null) {
// pattern match?
// Pattern match?
for (String registeredPath : this.cacheMappings.keySet()) {
if (this.pathMatcher.match(registeredPath, urlPath)) {
cacheSeconds = this.cacheMappings.get(registeredPath);

View File

@ -138,8 +138,7 @@ import org.springframework.web.util.WebUtils;
* @see #setMethodNameResolver
* @see #setWebBindingInitializer
* @see #setSessionAttributeStore
*
* @deprecated in Spring 3.2 in favor of
* @deprecated as of Spring 3.2, in favor of
* {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter RequestMappingHandlerAdapter}
*/
@Deprecated
@ -411,13 +410,10 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator
}
if (annotatedWithSessionAttributes) {
// Always prevent caching in case of session attribute management.
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers);
// Prepare cached set of session attributes names.
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
}
else {
// Uses configured default cacheSeconds setting.
checkAndPrepare(request, response);
checkAndPrepare(request, response, true);
}
// Execute invokeHandlerMethod in synchronized block if required.

View File

@ -705,13 +705,13 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
checkRequest(request);
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
// Always prevent caching in case of session attribute management.
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers);
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
// Uses configured default cacheSeconds setting.
checkAndPrepare(request, response);
prepareResponse(response);
}
// Execute invokeHandlerMethod in synchronized block if required.

View File

@ -39,9 +39,7 @@ import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRange;
import org.springframework.http.MediaType;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
@ -52,6 +50,8 @@ import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.support.WebContentGenerator;
@ -91,7 +91,8 @@ import org.springframework.web.servlet.support.WebContentGenerator;
* @author Arjen Poutsma
* @since 3.0.4
*/
public class ResourceHttpRequestHandler extends WebContentGenerator implements HttpRequestHandler, InitializingBean, CorsConfigurationSource {
public class ResourceHttpRequestHandler extends WebContentGenerator
implements HttpRequestHandler, InitializingBean, CorsConfigurationSource {
private static final String CONTENT_ENCODING = "Content-Encoding";
@ -171,6 +172,12 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
this.corsConfiguration = corsConfiguration;
}
@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
return this.corsConfiguration;
}
@Override
public void afterPropertiesSet() throws Exception {
if (logger.isWarnEnabled() && CollectionUtils.isEmpty(this.locations)) {
@ -180,11 +187,6 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
initAllowedLocations();
}
@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
return corsConfiguration;
}
/**
* Look for a {@link org.springframework.web.servlet.resource.PathResourceResolver}
* among the {@link #getResourceResolvers() resource resolvers} and configure
@ -207,6 +209,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
}
}
/**
* Processes a resource request.
* <p>Checks for the existence of the requested resource in the configured list of locations.
@ -223,9 +226,10 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
checkAndPrepare(request, response);
// Supported methods and required session
checkRequest(request);
// check whether a matching resource exists
// Check whether a matching resource exists
Resource resource = getResource(request);
if (resource == null) {
logger.trace("No matching resource found - returning 404");
@ -233,13 +237,16 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
return;
}
// header phase
// Header phase
if (new ServletWebRequest(request, response).checkNotModified(resource.lastModified())) {
logger.trace("Resource not modified - returning 304");
return;
}
// check the resource's media type
// Apply cache settings, if any
prepareResponse(response);
// Check the resource's media type
MediaType mediaType = getMediaType(resource);
if (mediaType != null) {
if (logger.isTraceEnabled()) {
@ -252,7 +259,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
}
}
// content phase
// Content phase
if (METHOD_HEAD.equals(request.getMethod())) {
setHeaders(response, resource, mediaType);
logger.trace("HEAD request - skipping content");
@ -532,15 +539,12 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
}
private void copyRange(InputStream in, OutputStream out, long start, long end) throws IOException {
long skipped = in.skip(start);
if (skipped < start) {
throw new IOException("Skipped only " + skipped + " bytes out of " + start + " required.");
}
long bytesToCopy = end - start + 1;
byte buffer[] = new byte[StreamUtils.BUFFER_SIZE];
while (bytesToCopy > 0) {
int bytesRead = in.read(buffer);
@ -561,8 +565,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
@Override
public String toString() {
return "ResourceHttpRequestHandler [locations=" +
getLocations() + ", resolvers=" + getResourceResolvers() + "]";
return "ResourceHttpRequestHandler [locations=" + getLocations() + ", resolvers=" + getResourceResolvers() + "]";
}

View File

@ -16,22 +16,18 @@
package org.springframework.web.servlet.support;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.CacheControl;
import org.springframework.util.StringUtils;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.HttpSessionRequiredException;
import org.springframework.http.CacheControl;
import org.springframework.web.context.support.WebApplicationObjectSupport;
/**
@ -41,19 +37,22 @@ import org.springframework.web.context.support.WebApplicationObjectSupport;
* Can also be used for custom handlers that have their own
* {@link org.springframework.web.servlet.HandlerAdapter}.
*
* <p>Supports HTTP cache control options. The usage of corresponding
* HTTP headers can be controlled via the "setCacheSeconds" or "setCacheControl" properties.
* As of 4.2, its default behavior changed when using only {@link #setCacheSeconds(int)}, sending
* HTTP response headers that are more in line with current browsers and proxies implementations.
* <p>Supports HTTP cache control options. The usage of corresponding HTTP
* headers can be controlled via the {@link #setCacheSeconds "cacheSeconds"
* and {@link #setCacheControl "cacheControl"} properties.
*
* <p>Reverting to the previous behavior can be easily done by using one of the nealy deprecated methods
* {@link #setUseExpiresHeader}, {@link #setUseCacheControlHeader}, {@link #setUseCacheControlNoStore} or
* {@link #setAlwaysMustRevalidate}.
* <p><b>NOTE:</b> As of Spring 4.2, this generator's default behavior changed when
* using only {@link #setCacheSeconds)}, sending HTTP response headers that are in line
* with current browsers and proxies implementations (i.e. no HTTP 1.0 headers anymore)
* Reverting to the previous behavior can be easily done by using one of the newly
* deprecated methods {@link #setUseExpiresHeader}, {@link #setUseCacheControlHeader},
* {@link #setUseCacheControlNoStore} or {@link #setAlwaysMustRevalidate}.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Brian Clozel
* @see #setCacheSeconds
* @see #setCacheControl
* @see #setRequireSession
*/
public abstract class WebContentGenerator extends WebApplicationObjectSupport {
@ -79,10 +78,12 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport {
private boolean requireSession = false;
private CacheControl cacheControl;
private int cacheSeconds = -1;
/** Use HTTP 1.0 expires header? */
private boolean useExpiresHeader = true;
private boolean useExpiresHeader = false;
/** Use HTTP 1.1 cache-control header? */
private boolean useCacheControlHeader = true;
@ -92,10 +93,6 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport {
private boolean alwaysMustRevalidate = false;
private boolean usePreviousHttpCachingBehavior = false;
private CacheControl cacheControl;
/**
* Create a new WebContentGenerator which supports
@ -167,40 +164,60 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport {
/**
* Set the {@link org.springframework.http.CacheControl} instance to build
* the Cache-Control HTTP response header.
*
* @since 4.2
*/
public void setCacheControl(CacheControl cacheControl) {
public final void setCacheControl(CacheControl cacheControl) {
this.cacheControl = cacheControl;
}
/**
* Get the {@link org.springframework.http.CacheControl} instance
* that builds the Cache-Control HTTP response header.
*
* @since 4.2
*/
public CacheControl getCacheControl() {
return cacheControl;
public final CacheControl getCacheControl() {
return this.cacheControl;
}
/**
* Set whether to use the HTTP 1.0 expires header. Default is "false".
* Cache content for the given number of seconds, by writing
* cache-related HTTP headers to the response:
* <ul>
* <li>seconds == -1 (default value): no generation cache-related headers</li>
* <li>seconds == 0: "Cache-Control: no-store" will prevent caching</li>
* <li>seconds > 0: "Cache-Control: max-age=seconds" will ask to cache content</li>
* </ul>
* <p>For more specific needs, a custom {@link org.springframework.http.CacheControl}
* should be used.
* @see #setCacheControl
*/
public final void setCacheSeconds(int seconds) {
this.cacheSeconds = seconds;
}
/**
* Return the number of seconds that content is cached.
*/
public final int getCacheSeconds() {
return this.cacheSeconds;
}
/**
* Set whether to use the HTTP 1.0 expires header. Default is "false",
* as of 4.2.
* <p>Note: Cache headers will only get applied if caching is enabled
* (or explicitly prevented) for the current request.
*
* @deprecated in favor of {@link #setCacheSeconds} or {@link #setCacheControl}.
* @deprecated as of 4.2, since going forward, the HTTP 1.1 cache-control
* header will be required, with the HTTP 1.0 headers disappearing
*/
@Deprecated
public final void setUseExpiresHeader(boolean useExpiresHeader) {
this.useExpiresHeader = useExpiresHeader;
this.usePreviousHttpCachingBehavior = true;
}
/**
* Return whether the HTTP 1.0 expires header is used.
*
* @deprecated in favor of {@link #getCacheControl}.
* @deprecated as of 4.2, in favor of {@link #getCacheControl()}
*/
@Deprecated
public final boolean isUseExpiresHeader() {
@ -211,19 +228,17 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport {
* Set whether to use the HTTP 1.1 cache-control header. Default is "true".
* <p>Note: Cache headers will only get applied if caching is enabled
* (or explicitly prevented) for the current request.
*
* @deprecated in favor of {@link #setCacheSeconds} or {@link #setCacheControl}.
* @deprecated as of 4.2, since going forward, the HTTP 1.1 cache-control
* header will be required, with the HTTP 1.0 headers disappearing
*/
@Deprecated
public final void setUseCacheControlHeader(boolean useCacheControlHeader) {
this.useCacheControlHeader = useCacheControlHeader;
this.usePreviousHttpCachingBehavior = true;
}
/**
* Return whether the HTTP 1.1 cache-control header is used.
*
* @deprecated in favor of {@link #getCacheControl}.
* @deprecated as of 4.2, in favor of {@link #getCacheControl()}
*/
@Deprecated
public final boolean isUseCacheControlHeader() {
@ -233,19 +248,16 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport {
/**
* Set whether to use the HTTP 1.1 cache-control header value "no-store"
* when preventing caching. Default is "true".
*
* @deprecated in favor of {@link #setCacheSeconds} or {@link #setCacheControl}.
* @deprecated as of 4.2, in favor of {@link #setCacheControl}
*/
@Deprecated
public final void setUseCacheControlNoStore(boolean useCacheControlNoStore) {
this.useCacheControlNoStore = useCacheControlNoStore;
this.usePreviousHttpCachingBehavior = true;
}
/**
* Return whether the HTTP 1.1 cache-control header value "no-store" is used.
*
* @deprecated in favor of {@link #getCacheControl}.
* @deprecated as of 4.2, in favor of {@link #getCacheControl()}
*/
@Deprecated
public final boolean isUseCacheControlNoStore() {
@ -253,125 +265,34 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport {
}
/**
* An option to add 'must-revalidate' to every Cache-Control header. This
* may be useful with annotated controller methods, which can
* programmatically do a lastModified calculation as described in
* {@link WebRequest#checkNotModified(long)}. Default is "false".
*
* @deprecated in favor of {@link #setCacheSeconds} or {@link #setCacheControl}.
* An option to add 'must-revalidate' to every Cache-Control header.
* This may be useful with annotated controller methods, which can
* programmatically do a last-modified calculation as described in
* {@link org.springframework.web.context.request.WebRequest#checkNotModified(long)}.
* <p>Default is "false".
* @deprecated as of 4.2, in favor of {@link #setCacheControl}
*/
@Deprecated
public void setAlwaysMustRevalidate(boolean mustRevalidate) {
public final void setAlwaysMustRevalidate(boolean mustRevalidate) {
this.alwaysMustRevalidate = mustRevalidate;
this.usePreviousHttpCachingBehavior = true;
}
/**
* Return whether 'must-revalidate' is added to every Cache-Control header.
*
* @deprecated in favor of {@link #getCacheControl}.
* @deprecated as of 4.2, in favor of {@link #getCacheControl()}
*/
@Deprecated
public boolean isAlwaysMustRevalidate() {
return alwaysMustRevalidate;
}
/**
* Cache content for the given number of seconds, by writing
* cache-related HTTP headers to the response:
* <ul>
* <li>seconds == -1 (default value): no generation cache-related headers</li>
* <li>seconds == 0: "Cache-Control: no-store" will prevent caching</li>
* <li>seconds > 0: "Cache-Control: max-age=seconds" will ask to cache content</li>
* </ul>
* <p>For more specific needs, a custom {@link org.springframework.http.CacheControl} should be used.
*
* @see #setCacheControl
*/
public final void setCacheSeconds(int seconds) {
this.cacheSeconds = seconds;
if (!this.usePreviousHttpCachingBehavior) {
if (cacheSeconds > 0) {
this.cacheControl = CacheControl.maxAge(seconds, TimeUnit.SECONDS);
}
else if (cacheSeconds == 0) {
this.cacheControl = CacheControl.noStore();
}
}
}
/**
* Return the number of seconds that content is cached.
*/
public final int getCacheSeconds() {
return this.cacheSeconds;
public final boolean isAlwaysMustRevalidate() {
return this.alwaysMustRevalidate;
}
/**
* Check and prepare the given request and response according to the settings
* of this generator. Checks for supported methods and a required session,
* and applies the number of cache seconds specified for this generator.
* Check the given request for supported methods and a required session, if any.
* @param request current HTTP request
* @param response current HTTP response
* @throws ServletException if the request cannot be handled because a check failed
* @since 4.2
*/
protected final void checkAndPrepare(
HttpServletRequest request, HttpServletResponse response)
throws ServletException {
checkAndPrepare(request, response, this.cacheControl);
}
/**
* * Check and prepare the given request and response according to the settings
* of this generator. Checks for supported methods and a required session,
* and applies the number of cache seconds specified for this generator.
* @param request current HTTP request
* @param response current HTTP response
* @param lastModified whether the underlying handler writes "Last-Modified" headers; in that case,
* a "Cache-Control: must-revalidate" directive is set in the response.
* @throws ServletException if the request cannot be handled because a check failed
* @deprecated in favor of {@link #checkAndPrepare(HttpServletRequest, HttpServletResponse, CacheControl)}.
*/
@Deprecated
protected final void checkAndPrepare(
HttpServletRequest request, HttpServletResponse response, boolean lastModified)
throws ServletException {
if (lastModified) {
checkAndPrepare(request, response, this.cacheControl.mustRevalidate());
}
else {
checkAndPrepare(request, response);
}
}
protected final void checkAndPrepare(
HttpServletRequest request, HttpServletResponse response, int cacheSeconds) throws ServletException {
CacheControl cControl;
if (cacheSeconds > 0) {
cControl = CacheControl.maxAge(cacheSeconds, TimeUnit.SECONDS);
}
else if (cacheSeconds == 0) {
cControl = CacheControl.noStore();
}
else {
cControl = CacheControl.empty();
}
checkRequest(request);
if (this.usePreviousHttpCachingBehavior) {
addHttp10CacheHeaders(cacheSeconds, response);
}
else {
String ccValue = cControl.getHeaderValue();
if (ccValue != null) {
response.setHeader(HEADER_CACHE_CONTROL, ccValue);
}
}
}
protected final void checkRequest(HttpServletRequest request) throws ServletException {
// Check whether we should support the request method.
String method = request.getMethod();
@ -387,46 +308,143 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport {
}
/**
* Check and prepare the given request and response according to the settings
* of this generator. Checks for supported methods and a required session
* specified for this generator. Also applies the {@link org.springframework.http.CacheControl}
* given as a parameter.
* @param request current HTTP request
* Prepare the given response according to the settings of this generator.
* Applies the number of cache seconds specified for this generator.
* @param response current HTTP response
* @param cacheControl the {@link org.springframework.http.CacheControl} to use
* @throws ServletException if the request cannot be handled because a check failed
*
* @since 4.2
*/
protected final void checkAndPrepare(
HttpServletRequest request, HttpServletResponse response, CacheControl cacheControl)
throws ServletException {
checkRequest(request);
if (this.usePreviousHttpCachingBehavior) {
addHttp10CacheHeaders(this.cacheSeconds, response);
protected final void prepareResponse(HttpServletResponse response) {
if (this.cacheControl != null) {
applyCacheControl(response, this.cacheControl);
}
else if (cacheControl != null) {
String ccValue = cacheControl.getHeaderValue();
if (ccValue != null) {
response.setHeader(HEADER_CACHE_CONTROL, ccValue);
if (response.containsHeader(HEADER_PRAGMA)) {
response.setHeader(HEADER_PRAGMA, "");
}
else {
applyCacheSeconds(response, this.cacheSeconds);
}
}
/**
* Set the HTTP Cache-Control header according to the given settings.
* @param response current HTTP response
* @param cacheControl the pre-configured cache control settings
* @since 4.2
*/
protected final void applyCacheControl(HttpServletResponse response, CacheControl cacheControl) {
String ccValue = cacheControl.getHeaderValue();
if (ccValue != null) {
// Set computed HTTP 1.1 Cache-Control header
response.setHeader(HEADER_CACHE_CONTROL, ccValue);
if (response.containsHeader(HEADER_PRAGMA)) {
// Reset HTTP 1.0 Pragma header if present
response.setHeader(HEADER_PRAGMA, "");
}
}
}
protected final void addHttp10CacheHeaders(int cacheSeconds, HttpServletResponse response) {
/**
* Apply the given cache seconds and generate corresponding HTTP headers,
* i.e. allow caching for the given number of seconds in case of a positive
* value, prevent caching if given a 0 value, do nothing else.
* Does not tell the browser to revalidate the resource.
* @param response current HTTP response
* @param cacheSeconds positive number of seconds into the future that the
* response should be cacheable for, 0 to prevent caching
*/
@SuppressWarnings("deprecation")
protected final void applyCacheSeconds(HttpServletResponse response, int cacheSeconds) {
if (this.useExpiresHeader || !this.useCacheControlHeader) {
// Deprecated HTTP 1.0 cache behavior, as in previous Spring versions
if (cacheSeconds > 0) {
cacheForSeconds(response, cacheSeconds);
}
else if (cacheSeconds == 0) {
preventCaching(response);
}
}
else {
CacheControl cControl;
if (cacheSeconds > 0) {
cControl = CacheControl.maxAge(cacheSeconds, TimeUnit.SECONDS);
if (this.alwaysMustRevalidate) {
cControl = cControl.mustRevalidate();
}
}
else if (cacheSeconds == 0) {
cControl = (this.useCacheControlNoStore ? CacheControl.noStore() : CacheControl.noCache());
}
else {
cControl = CacheControl.empty();
}
applyCacheControl(response, cControl);
}
}
/**
* @see #checkRequest(HttpServletRequest)
* @see #prepareResponse(HttpServletResponse)
* @deprecated as of 4.2, since the {@code lastModified} flag is effectively ignored,
* with a must-revalidate header only generated if explicitly configured
*/
@Deprecated
protected final void checkAndPrepare(
HttpServletRequest request, HttpServletResponse response, boolean lastModified) throws ServletException {
checkRequest(request);
prepareResponse(response);
}
/**
* @see #checkRequest(HttpServletRequest)
* @see #applyCacheSeconds(HttpServletResponse, int)
* @deprecated as of 4.2, since the {@code lastModified} flag is effectively ignored,
* with a must-revalidate header only generated if explicitly configured
*/
@Deprecated
protected final void checkAndPrepare(
HttpServletRequest request, HttpServletResponse response, int cacheSeconds, boolean lastModified)
throws ServletException {
checkRequest(request);
applyCacheSeconds(response, cacheSeconds);
}
/**
* Apply the given cache seconds and generate respective HTTP headers.
* <p>That is, allow caching for the given number of seconds in the
* case of a positive value, prevent caching if given a 0 value, else
* do nothing (i.e. leave caching to the client).
* @param response the current HTTP response
* @param cacheSeconds the (positive) number of seconds into the future
* that the response should be cacheable for; 0 to prevent caching; and
* a negative value to leave caching to the client.
* @param mustRevalidate whether the client should revalidate the resource
* (typically only necessary for controllers with last-modified support)
* @deprecated as of 4.2, in favor of {@link #applyCacheControl}
*/
@Deprecated
protected final void applyCacheSeconds(HttpServletResponse response, int cacheSeconds, boolean mustRevalidate) {
if (cacheSeconds > 0) {
cacheForSeconds(response, cacheSeconds, this.alwaysMustRevalidate);
cacheForSeconds(response, cacheSeconds, mustRevalidate);
}
else if (cacheSeconds == 0) {
preventCaching(response);
}
}
/**
* Set HTTP headers to allow caching for the given number of seconds.
* Does not tell the browser to revalidate the resource.
* @param response current HTTP response
* @param seconds number of seconds into the future that the response
* should be cacheable for
* @deprecated as of 4.2, in favor of {@link #applyCacheControl}
*/
@Deprecated
protected final void cacheForSeconds(HttpServletResponse response, int seconds) {
cacheForSeconds(response, seconds, false);
}
/**
* Set HTTP headers to allow caching for the given number of seconds.
* Tells the browser to revalidate the resource if mustRevalidate is
@ -436,38 +454,48 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport {
* should be cacheable for
* @param mustRevalidate whether the client should revalidate the resource
* (typically only necessary for controllers with last-modified support)
* @deprecated as of 4.2, in favor of {@link #applyCacheControl}
*/
@Deprecated
protected final void cacheForSeconds(HttpServletResponse response, int seconds, boolean mustRevalidate) {
if (this.useExpiresHeader) {
// HTTP 1.0 header
response.setDateHeader(HEADER_EXPIRES, System.currentTimeMillis() + seconds * 1000L);
}
if (this.useCacheControlHeader) {
// HTTP 1.1 header
String headerValue = "max-age=" + seconds;
if (mustRevalidate) {
if (mustRevalidate || this.alwaysMustRevalidate) {
headerValue += ", must-revalidate";
}
response.setHeader(HEADER_CACHE_CONTROL, headerValue);
}
if (response.containsHeader(HEADER_PRAGMA)) {
// Reset HTTP 1.0 Pragma header if present
response.setHeader(HEADER_PRAGMA, "");
}
}
/**
* Prevent the response from being cached.
* See {@code http://www.mnot.net/cache_docs}.
* Only called in HTTP 1.0 compatibility mode.
* <p>See {@code http://www.mnot.net/cache_docs}.
* @deprecated as of 4.2, in favor of {@link #applyCacheControl}
*/
@Deprecated
protected final void preventCaching(HttpServletResponse response) {
response.setHeader(HEADER_PRAGMA, "no-cache");
if (this.useExpiresHeader) {
// HTTP 1.0 header
response.setDateHeader(HEADER_EXPIRES, System.currentTimeMillis());
// HTTP 1.0 Expires header
response.setDateHeader(HEADER_EXPIRES, 1L);
}
if (this.useCacheControlHeader) {
// HTTP 1.1 header: "no-cache" is the standard value,
// "no-store" is necessary to prevent caching on FireFox.
// HTTP 1.1 Cache-Control header: "no-cache" is the standard value,
// "no-store" is necessary to prevent caching on Firefox.
response.setHeader(HEADER_CACHE_CONTROL, "no-cache");
if (this.useCacheControlNoStore) {
response.addHeader(HEADER_CACHE_CONTROL, "no-store");

View File

@ -392,8 +392,6 @@ public class MvcNamespaceTests {
ResourceHttpRequestHandler.class);
assertNotNull(handler);
assertEquals(3600, handler.getCacheSeconds());
assertThat(handler.getCacheControl().getHeaderValue(),
Matchers.equalTo(CacheControl.maxAge(1, TimeUnit.HOURS).getHeaderValue()));
}
@Test

View File

@ -90,8 +90,6 @@ public class ResourceHandlerRegistryTests {
this.registration.setCachePeriod(0);
assertEquals(0, getHandler("/resources/**").getCacheSeconds());
assertThat(getHandler("/resources/**").getCacheControl().getHeaderValue(),
Matchers.equalTo(CacheControl.noStore().getHeaderValue()));
}
@Test

View File

@ -137,6 +137,7 @@ public class WebContentInterceptorTests {
public void http10CachingConfigAndSpecificMapping() throws Exception {
WebContentInterceptor interceptor = new WebContentInterceptor();
interceptor.setCacheSeconds(0);
interceptor.setUseExpiresHeader(true);
interceptor.setAlwaysMustRevalidate(true);
Properties mappings = new Properties();
mappings.setProperty("**/*.cache.html", "10");

View File

@ -110,7 +110,7 @@ public class ResourceHttpRequestHandlerTests {
@Test
@SuppressWarnings("deprecation")
public void getResourcePreviousBehaviorCache() throws Exception {
public void getResourceHttp10BehaviorCache() throws Exception {
this.request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "foo.css");
this.handler.setCacheSeconds(3600);
this.handler.setUseExpiresHeader(true);
@ -126,15 +126,17 @@ public class ResourceHttpRequestHandlerTests {
@Test
@SuppressWarnings("deprecation")
public void getResourcePreviousBehaviorNoCache() throws Exception {
public void getResourceHttp10BehaviorNoCache() throws Exception {
this.request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "foo.css");
this.handler.setCacheSeconds(0);
this.handler.setUseCacheControlNoStore(true);
this.handler.setUseExpiresHeader(true);
this.handler.setUseCacheControlNoStore(false);
this.handler.setUseCacheControlHeader(true);
this.handler.handleRequest(this.request, this.response);
assertEquals("no-cache", this.response.getHeader("Pragma"));
assertThat(this.response.getHeaderValues("Cache-Control"), Matchers.contains("no-cache", "no-store"));
assertThat(this.response.getHeaderValues("Cache-Control"), Matchers.iterableWithSize(1));
assertEquals("no-cache", this.response.getHeader("Cache-Control"));
assertTrue(dateHeaderAsLong("Expires") <= System.currentTimeMillis());
assertTrue(this.response.containsHeader("Last-Modified"));
assertEquals(dateHeaderAsLong("Last-Modified") / 1000, resourceLastModified("test/foo.css") / 1000);