Set ETag header with VersionResourceResolver

Prior to this change, VersionResourceResolver and VersionStrategy would
resolve static resources using version strings. They assist
ResourceHttpRequestHandler with serving static resources. The
RequestHandler itself can be configured with HTTP caching strategies to
set Cache-Control headers.

In order to have a complete strategy with Cache-Control and ETag
response headers, developers can't reuse that version string information
and have to rely on other mechanisms (like ShallowEtagHeaderFilter).

This commit makes VersionResourceResolver use that version string to set
it as a request attribute, which will be used by the
ResourceHttpRequestHandler to write an ETag response header.

Issue: SPR-13382
This commit is contained in:
Brian Clozel
2015-08-25 16:49:10 +02:00
parent 0b9c3de320
commit 190eb6ace1
5 changed files with 37 additions and 9 deletions

View File

@@ -89,13 +89,12 @@ import org.springframework.web.servlet.support.WebContentGenerator;
* @author Jeremy Grelle
* @author Juergen Hoeller
* @author Arjen Poutsma
* @author Brian Clozel
* @since 3.0.4
*/
public class ResourceHttpRequestHandler extends WebContentGenerator
implements HttpRequestHandler, InitializingBean, CorsConfigurationSource {
private static final String CONTENT_ENCODING = "Content-Encoding";
private static final Log logger = LogFactory.getLog(ResourceHttpRequestHandler.class);
private static final boolean jafPresent = ClassUtils.isPresent(
@@ -267,6 +266,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
}
if (request.getHeader(HttpHeaders.RANGE) == null) {
setETagHeader(request, response);
setHeaders(response, resource, mediaType);
writeContent(response, resource);
}
@@ -406,6 +406,21 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
return mediaType;
}
/**
* Set the ETag header if the version string of the served resource is present.
* Version strings can be resolved by {@link VersionStrategy} implementations and then
* set as a request attribute by {@link VersionResourceResolver}.
* @param request current servlet request
* @param response current servlet response
* @see VersionResourceResolver
*/
protected void setETagHeader(HttpServletRequest request, HttpServletResponse response) {
String versionString = (String) request.getAttribute(VersionResourceResolver.RESOURCE_VERSION_ATTRIBUTE);
if(versionString != null) {
response.setHeader(HttpHeaders.ETAG, "\"" + versionString + "\"");
}
}
/**
* Set headers on the given servlet response.
* Called for GET requests as well as HEAD requests.
@@ -426,7 +441,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
}
if (resource instanceof EncodedResource) {
response.setHeader(CONTENT_ENCODING, ((EncodedResource) resource).getContentEncoding());
response.setHeader(HttpHeaders.CONTENT_ENCODING, ((EncodedResource) resource).getContentEncoding());
}
response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@@ -54,6 +54,8 @@ import org.springframework.util.StringUtils;
*/
public class VersionResourceResolver extends AbstractResourceResolver {
public static final String RESOURCE_VERSION_ATTRIBUTE = VersionResourceResolver.class.getName() + ".resourceVersion";
private AntPathMatcher pathMatcher = new AntPathMatcher();
/** Map from path pattern -> VersionStrategy */
@@ -165,6 +167,9 @@ public class VersionResourceResolver extends AbstractResourceResolver {
if (logger.isTraceEnabled()) {
logger.trace("resource matches extracted version");
}
if(request != null) {
request.setAttribute(VersionResourceResolver.RESOURCE_VERSION_ATTRIBUTE, candidateVersion);
}
return baseResource;
}
else {