Align MVC checkNotModified with reactive support

Since SPR-14522, the web reactive framework supports checkNotModified
features. This commit aligns the existing MVC infrastructure with
web reactive's behavior.

Because of the new Servlet 3.0 baseline, some constraints
aren't relevant anymore and duplicate code has been removed in
`HttpEntityMethodProcessor`.

Issue: SPR-14659
This commit is contained in:
Brian Clozel
2016-09-06 14:32:14 +02:00
parent 72e4fac28d
commit cc5300c4d5
4 changed files with 172 additions and 224 deletions

View File

@@ -28,8 +28,6 @@ import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
@@ -41,6 +39,7 @@ import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
@@ -182,15 +181,15 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
}
if (responseEntity instanceof ResponseEntity) {
outputMessage.getServletResponse().setStatus(((ResponseEntity<?>) responseEntity).getStatusCodeValue());
HttpMethod method = inputMessage.getMethod();
boolean isGetOrHead = (HttpMethod.GET == method || HttpMethod.HEAD == method);
if (isGetOrHead && isResourceNotModified(inputMessage, outputMessage)) {
outputMessage.setStatusCode(HttpStatus.NOT_MODIFIED);
// Ensure headers are flushed, no body should be written.
outputMessage.flush();
// Skip call to converters, as they may update the body.
return;
int returnStatus = ((ResponseEntity<?>) responseEntity).getStatusCodeValue();
outputMessage.getServletResponse().setStatus(returnStatus);
if(returnStatus == 200) {
if (isResourceNotModified(inputMessage, outputMessage)) {
// Ensure headers are flushed, no body should be written.
outputMessage.flush();
// Skip call to converters, as they may update the body.
return;
}
}
}
@@ -223,55 +222,15 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
}
private boolean isResourceNotModified(ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) {
boolean notModified = false;
try {
long ifModifiedSince = inputMessage.getHeaders().getIfModifiedSince();
String eTag = addEtagPadding(outputMessage.getHeaders().getETag());
long lastModified = outputMessage.getHeaders().getLastModified();
List<String> ifNoneMatch = inputMessage.getHeaders().getIfNoneMatch();
if (!ifNoneMatch.isEmpty() && (inputMessage.getHeaders().containsKey(HttpHeaders.IF_UNMODIFIED_SINCE)
|| inputMessage.getHeaders().containsKey(HttpHeaders.IF_MATCH))) {
// invalid conditional request, do not process
}
else if (lastModified != -1 && StringUtils.hasLength(eTag)) {
notModified = isETagNotModified(ifNoneMatch, eTag) && isTimeStampNotModified(ifModifiedSince, lastModified);
}
else if (lastModified != -1) {
notModified = isTimeStampNotModified(ifModifiedSince, lastModified);
}
else if (StringUtils.hasLength(eTag)) {
notModified = isETagNotModified(ifNoneMatch, eTag);
}
}
catch (IllegalArgumentException exc) {
// invalid conditional request, do not process
}
return notModified;
}
ServletWebRequest servletWebRequest =
new ServletWebRequest(inputMessage.getServletRequest(), outputMessage.getServletResponse());
HttpHeaders responseHeaders = outputMessage.getHeaders();
String etag = responseHeaders.getETag();
long lastModifiedTimestamp = responseHeaders.getLastModified();
responseHeaders.remove(HttpHeaders.ETAG);
responseHeaders.remove(HttpHeaders.LAST_MODIFIED);
private boolean isETagNotModified(List<String> ifNoneMatch, String etag) {
if (StringUtils.hasLength(etag)) {
for (String clientETag : ifNoneMatch) {
// Compare weak/strong ETags as per https://tools.ietf.org/html/rfc7232#section-2.3
if (StringUtils.hasLength(clientETag) &&
(clientETag.replaceFirst("^W/", "").equals(etag.replaceFirst("^W/", "")))) {
return true;
}
}
}
return false;
}
private boolean isTimeStampNotModified(long ifModifiedSince, long lastModifiedTimestamp) {
return (ifModifiedSince >= (lastModifiedTimestamp / 1000 * 1000));
}
private String addEtagPadding(String etag) {
if (StringUtils.hasLength(etag) &&
(!(etag.startsWith("\"") || etag.startsWith("W/\"")) || !etag.endsWith("\"")) ) {
etag = "\"" + etag + "\"";
}
return etag;
return servletWebRequest.checkNotModified(etag, lastModifiedTimestamp);
}
@Override