Add support for setting the "Vary" response header
Issue: SPR-14070
This commit is contained in:
@@ -19,7 +19,10 @@ package org.springframework.web.servlet.mvc.method.annotation;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
@@ -40,6 +43,8 @@ import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
import static org.springframework.http.HttpHeaders.VARY;
|
||||
|
||||
/**
|
||||
* Resolves {@link HttpEntity} and {@link RequestEntity} method argument values
|
||||
* and also handles {@link HttpEntity} and {@link ResponseEntity} return values.
|
||||
@@ -162,9 +167,18 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
||||
Assert.isInstanceOf(HttpEntity.class, returnValue);
|
||||
HttpEntity<?> responseEntity = (HttpEntity<?>) returnValue;
|
||||
|
||||
HttpHeaders outputHeaders = outputMessage.getHeaders();
|
||||
HttpHeaders entityHeaders = responseEntity.getHeaders();
|
||||
if (outputHeaders.containsKey(VARY) && entityHeaders.containsKey(VARY)) {
|
||||
List<String> values = getVaryRequestHeadersToAdd(outputHeaders, entityHeaders);
|
||||
if (!values.isEmpty()) {
|
||||
outputHeaders.setVary(values);
|
||||
}
|
||||
}
|
||||
if (!entityHeaders.isEmpty()) {
|
||||
outputMessage.getHeaders().putAll(entityHeaders);
|
||||
for (Map.Entry<String, List<String>> entry : entityHeaders.entrySet()) {
|
||||
outputHeaders.putIfAbsent(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
Object body = responseEntity.getBody();
|
||||
@@ -188,6 +202,27 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
||||
outputMessage.flush();
|
||||
}
|
||||
|
||||
private List<String> getVaryRequestHeadersToAdd(HttpHeaders responseHeaders, HttpHeaders entityHeaders) {
|
||||
if (!responseHeaders.containsKey(HttpHeaders.VARY)) {
|
||||
return entityHeaders.getVary();
|
||||
}
|
||||
List<String> entityHeadersVary = entityHeaders.getVary();
|
||||
List<String> result = new ArrayList<String>(entityHeadersVary);
|
||||
for (String header : responseHeaders.get(HttpHeaders.VARY)) {
|
||||
for (String existing : StringUtils.tokenizeToStringArray(header, ",")) {
|
||||
if ("*".equals(existing)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
for (String value : entityHeadersVary) {
|
||||
if (value.equalsIgnoreCase(existing)) {
|
||||
result.remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isResourceNotModified(ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) {
|
||||
List<String> ifNoneMatch = inputMessage.getHeaders().getIfNoneMatch();
|
||||
long ifModifiedSince = inputMessage.getHeaders().getIfModifiedSince();
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.web.servlet.support;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -27,7 +28,9 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
@@ -77,6 +80,10 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport {
|
||||
|
||||
protected static final String HEADER_CACHE_CONTROL = "Cache-Control";
|
||||
|
||||
/** Checking for Servlet 3.0+ HttpServletResponse.getHeaders(String) */
|
||||
private static final boolean servlet3Present =
|
||||
ClassUtils.hasMethod(HttpServletResponse.class, "getHeaders", String.class);
|
||||
|
||||
|
||||
/** Set of supported HTTP methods */
|
||||
private Set<String> supportedMethods;
|
||||
@@ -89,6 +96,11 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport {
|
||||
|
||||
private int cacheSeconds = -1;
|
||||
|
||||
private String[] varyByRequestHeaders;
|
||||
|
||||
|
||||
// deprecated fields
|
||||
|
||||
/** Use HTTP 1.0 expires header? */
|
||||
private boolean useExpiresHeader = false;
|
||||
|
||||
@@ -245,6 +257,29 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport {
|
||||
return this.cacheSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure one or more request header names (e.g. "Accept-Language") to
|
||||
* add to the "Vary" response header to inform clients that the response is
|
||||
* subject to content negotiation and variances based on the value of the
|
||||
* given request headers. The configured request header names are added only
|
||||
* if not already present in the response "Vary" header.
|
||||
*
|
||||
* <p><strong>Note:</strong> this property is only supported on Servlet 3.0+
|
||||
* which allows checking existing response header values.
|
||||
* @param varyByRequestHeaders one or more request header names
|
||||
* @since 4.3
|
||||
*/
|
||||
public void setVaryByRequestHeaders(String... varyByRequestHeaders) {
|
||||
this.varyByRequestHeaders = varyByRequestHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured request header names for the "Vary" response header.
|
||||
*/
|
||||
public String[] getVaryByRequestHeaders() {
|
||||
return this.varyByRequestHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to use the HTTP 1.0 expires header. Default is "false",
|
||||
* as of 4.2.
|
||||
@@ -363,6 +398,11 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport {
|
||||
else {
|
||||
applyCacheSeconds(response, this.cacheSeconds);
|
||||
}
|
||||
if (servlet3Present && this.varyByRequestHeaders != null) {
|
||||
for (String value : getVaryRequestHeadersToAdd(response)) {
|
||||
response.addHeader("Vary", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -546,4 +586,25 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport {
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<String> getVaryRequestHeadersToAdd(HttpServletResponse response) {
|
||||
if (!response.containsHeader(HttpHeaders.VARY)) {
|
||||
return Arrays.asList(getVaryByRequestHeaders());
|
||||
}
|
||||
Collection<String> result = new ArrayList<String>(getVaryByRequestHeaders().length);
|
||||
Collections.addAll(result, getVaryByRequestHeaders());
|
||||
for (String header : response.getHeaders(HttpHeaders.VARY)) {
|
||||
for (String existing : StringUtils.tokenizeToStringArray(header, ",")) {
|
||||
if ("*".equals(existing)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
for (String value : getVaryByRequestHeaders()) {
|
||||
if (value.equalsIgnoreCase(existing)) {
|
||||
result.remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user