SPR-7695 - Add ETag version of WebRequest.checkNotModified()
This commit is contained in:
parent
095a36e853
commit
7cc3f49910
|
|
@ -177,6 +177,14 @@ public class PortletWebRequest extends PortletRequestAttributes implements Nativ
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last-modified handling not supported for portlet requests:
|
||||||
|
* As a consequence, this method always returns <code>false</code>.
|
||||||
|
*/
|
||||||
|
public boolean checkNotModified(String eTag) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public String getDescription(boolean includeClientInfo) {
|
public String getDescription(boolean includeClientInfo) {
|
||||||
PortletRequest request = getRequest();
|
PortletRequest request = getRequest();
|
||||||
StringBuilder result = new StringBuilder();
|
StringBuilder result = new StringBuilder();
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,10 @@ public class FacesWebRequest extends FacesRequestAttributes implements NativeWeb
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean checkNotModified(String eTag) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public String getDescription(boolean includeClientInfo) {
|
public String getDescription(boolean includeClientInfo) {
|
||||||
ExternalContext externalContext = getExternalContext();
|
ExternalContext externalContext = getExternalContext();
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,16 @@ import org.springframework.util.StringUtils;
|
||||||
*/
|
*/
|
||||||
public class ServletWebRequest extends ServletRequestAttributes implements NativeWebRequest {
|
public class ServletWebRequest extends ServletRequestAttributes implements NativeWebRequest {
|
||||||
|
|
||||||
|
private static final String HEADER_ETAG = "ETag";
|
||||||
|
|
||||||
private static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since";
|
private static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since";
|
||||||
|
|
||||||
|
private static final String HEADER_IF_NONE_MATCH = "If-None-Match";
|
||||||
|
|
||||||
private static final String HEADER_LAST_MODIFIED = "Last-Modified";
|
private static final String HEADER_LAST_MODIFIED = "Last-Modified";
|
||||||
|
|
||||||
|
private static final String METHOD_GET = "GET";
|
||||||
|
|
||||||
|
|
||||||
private HttpServletResponse response;
|
private HttpServletResponse response;
|
||||||
|
|
||||||
|
|
@ -186,7 +192,7 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
|
||||||
long ifModifiedSince = getRequest().getDateHeader(HEADER_IF_MODIFIED_SINCE);
|
long ifModifiedSince = getRequest().getDateHeader(HEADER_IF_MODIFIED_SINCE);
|
||||||
this.notModified = (ifModifiedSince >= (lastModifiedTimestamp / 1000 * 1000));
|
this.notModified = (ifModifiedSince >= (lastModifiedTimestamp / 1000 * 1000));
|
||||||
if (this.response != null) {
|
if (this.response != null) {
|
||||||
if (this.notModified && "GET".equals(getRequest().getMethod())) {
|
if (this.notModified && METHOD_GET.equals(getRequest().getMethod())) {
|
||||||
this.response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
this.response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -197,6 +203,30 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
|
||||||
return this.notModified;
|
return this.notModified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean checkNotModified(String eTag) {
|
||||||
|
if (StringUtils.hasLength(eTag) && !this.notModified &&
|
||||||
|
(this.response == null || !this.response.containsHeader(HEADER_ETAG))) {
|
||||||
|
if (!eTag.startsWith("\"")) {
|
||||||
|
eTag = "\"" + eTag;
|
||||||
|
}
|
||||||
|
if (!eTag.endsWith("\"")) {
|
||||||
|
eTag = eTag + "\"";
|
||||||
|
}
|
||||||
|
String ifNoneMatch = getRequest().getHeader(HEADER_IF_NONE_MATCH);
|
||||||
|
this.notModified = eTag.equals(ifNoneMatch);
|
||||||
|
if (this.response != null) {
|
||||||
|
if (this.notModified && METHOD_GET.equals(getRequest().getMethod())) {
|
||||||
|
this.response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.response.setHeader(HEADER_ETAG, eTag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.notModified;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isNotModified() {
|
public boolean isNotModified() {
|
||||||
return this.notModified;
|
return this.notModified;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,9 @@ public interface WebRequest extends RequestAttributes {
|
||||||
* model.addAttribute(...);
|
* model.addAttribute(...);
|
||||||
* return "myViewName";
|
* return "myViewName";
|
||||||
* }</pre>
|
* }</pre>
|
||||||
|
* <p><strong>Note:</strong> that you typically want to use either
|
||||||
|
* this {@link #checkNotModified(long)} method; or
|
||||||
|
* {@link #checkNotModified(String)}, but not both.
|
||||||
* @param lastModifiedTimestamp the last-modified timestamp that
|
* @param lastModifiedTimestamp the last-modified timestamp that
|
||||||
* the application determined for the underlying resource
|
* the application determined for the underlying resource
|
||||||
* @return whether the request qualifies as not modified,
|
* @return whether the request qualifies as not modified,
|
||||||
|
|
@ -149,6 +152,35 @@ public interface WebRequest extends RequestAttributes {
|
||||||
*/
|
*/
|
||||||
boolean checkNotModified(long lastModifiedTimestamp);
|
boolean checkNotModified(long lastModifiedTimestamp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the request qualifies as not modified given the
|
||||||
|
* supplied {@code ETag} (entity tag), as determined by the application.
|
||||||
|
* <p>This will also transparently set the appropriate response headers,
|
||||||
|
* for both the modified case and the not-modified case.
|
||||||
|
* <p>Typical usage:
|
||||||
|
* <pre class="code">
|
||||||
|
* public String myHandleMethod(WebRequest webRequest, Model model) {
|
||||||
|
* String eTag = // application-specific calculation
|
||||||
|
* if (request.checkNotModified(eTag)) {
|
||||||
|
* // shortcut exit - no further processing necessary
|
||||||
|
* return null;
|
||||||
|
* }
|
||||||
|
* // further request processing, actually building content
|
||||||
|
* model.addAttribute(...);
|
||||||
|
* return "myViewName";
|
||||||
|
* }</pre>
|
||||||
|
* <p><strong>Note:</strong> that you typically want to use either
|
||||||
|
* this {@link #checkNotModified(String)} method; or
|
||||||
|
* {@link #checkNotModified(long)}, but not both.
|
||||||
|
* @param eTag the entity tag that the application determined
|
||||||
|
* for the underlying resource. This parameter will be padded
|
||||||
|
* with quotes (") if necessary.
|
||||||
|
* @return whether the request qualifies as not modified,
|
||||||
|
* allowing to abort request processing and relying on the response
|
||||||
|
* telling the client that the content has not been modified
|
||||||
|
*/
|
||||||
|
boolean checkNotModified(String eTag);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a short description of this request,
|
* Get a short description of this request,
|
||||||
* typically containing request URI and session id.
|
* typically containing request URI and session id.
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.web.context.request;
|
package org.springframework.web.context.request;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.servlet.ServletRequest;
|
import javax.servlet.ServletRequest;
|
||||||
|
|
@ -25,27 +26,40 @@ import javax.servlet.http.HttpServletRequestWrapper;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import javax.servlet.http.HttpServletResponseWrapper;
|
import javax.servlet.http.HttpServletResponseWrapper;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.mock.web.MockHttpServletResponse;
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
import org.springframework.web.multipart.MultipartRequest;
|
import org.springframework.web.multipart.MultipartRequest;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @since 26.07.2006
|
* @since 26.07.2006
|
||||||
*/
|
*/
|
||||||
public class ServletWebRequestTests {
|
public class ServletWebRequestTests {
|
||||||
|
|
||||||
|
private MockHttpServletRequest servletRequest;
|
||||||
|
|
||||||
|
private MockHttpServletResponse servletResponse;
|
||||||
|
|
||||||
|
private ServletWebRequest request;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
servletRequest = new MockHttpServletRequest();
|
||||||
|
servletResponse = new MockHttpServletResponse();
|
||||||
|
request = new ServletWebRequest(servletRequest, servletResponse);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testParameters() {
|
public void parameters() {
|
||||||
MockHttpServletRequest servletRequest = new MockHttpServletRequest();
|
|
||||||
servletRequest.addParameter("param1", "value1");
|
servletRequest.addParameter("param1", "value1");
|
||||||
servletRequest.addParameter("param2", "value2");
|
servletRequest.addParameter("param2", "value2");
|
||||||
servletRequest.addParameter("param2", "value2a");
|
servletRequest.addParameter("param2", "value2a");
|
||||||
|
|
||||||
ServletWebRequest request = new ServletWebRequest(servletRequest);
|
|
||||||
assertEquals("value1", request.getParameter("param1"));
|
assertEquals("value1", request.getParameter("param1"));
|
||||||
assertEquals(1, request.getParameterValues("param1").length);
|
assertEquals(1, request.getParameterValues("param1").length);
|
||||||
assertEquals("value1", request.getParameterValues("param1")[0]);
|
assertEquals("value1", request.getParameterValues("param1")[0]);
|
||||||
|
|
@ -64,19 +78,14 @@ public class ServletWebRequestTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocale() {
|
public void locale() {
|
||||||
MockHttpServletRequest servletRequest = new MockHttpServletRequest();
|
|
||||||
servletRequest.addPreferredLocale(Locale.UK);
|
servletRequest.addPreferredLocale(Locale.UK);
|
||||||
|
|
||||||
ServletWebRequest request = new ServletWebRequest(servletRequest);
|
|
||||||
assertEquals(Locale.UK, request.getLocale());
|
assertEquals(Locale.UK, request.getLocale());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNativeRequest() {
|
public void nativeRequest() {
|
||||||
MockHttpServletRequest servletRequest = new MockHttpServletRequest();
|
|
||||||
MockHttpServletResponse servletResponse = new MockHttpServletResponse();
|
|
||||||
ServletWebRequest request = new ServletWebRequest(servletRequest, servletResponse);
|
|
||||||
assertSame(servletRequest, request.getNativeRequest());
|
assertSame(servletRequest, request.getNativeRequest());
|
||||||
assertSame(servletRequest, request.getNativeRequest(ServletRequest.class));
|
assertSame(servletRequest, request.getNativeRequest(ServletRequest.class));
|
||||||
assertSame(servletRequest, request.getNativeRequest(HttpServletRequest.class));
|
assertSame(servletRequest, request.getNativeRequest(HttpServletRequest.class));
|
||||||
|
|
@ -90,9 +99,7 @@ public class ServletWebRequestTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDecoratedNativeRequest() {
|
public void decoratedNativeRequest() {
|
||||||
MockHttpServletRequest servletRequest = new MockHttpServletRequest();
|
|
||||||
MockHttpServletResponse servletResponse = new MockHttpServletResponse();
|
|
||||||
HttpServletRequest decoratedRequest = new HttpServletRequestWrapper(servletRequest);
|
HttpServletRequest decoratedRequest = new HttpServletRequestWrapper(servletRequest);
|
||||||
HttpServletResponse decoratedResponse = new HttpServletResponseWrapper(servletResponse);
|
HttpServletResponse decoratedResponse = new HttpServletResponseWrapper(servletResponse);
|
||||||
ServletWebRequest request = new ServletWebRequest(decoratedRequest, decoratedResponse);
|
ServletWebRequest request = new ServletWebRequest(decoratedRequest, decoratedResponse);
|
||||||
|
|
@ -108,4 +115,65 @@ public class ServletWebRequestTests {
|
||||||
assertNull(request.getNativeResponse(MultipartRequest.class));
|
assertNull(request.getNativeResponse(MultipartRequest.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkNotModifiedTimeStamp() {
|
||||||
|
long currentTime = new Date().getTime();
|
||||||
|
servletRequest.setMethod("GET");
|
||||||
|
servletRequest.addHeader("If-Modified-Since", currentTime);
|
||||||
|
|
||||||
|
request.checkNotModified(currentTime);
|
||||||
|
|
||||||
|
assertEquals(304, servletResponse.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkModifiedTimeStamp() {
|
||||||
|
long currentTime = new Date().getTime();
|
||||||
|
long oneMinuteAgo = currentTime - (1000 * 60);
|
||||||
|
servletRequest.setMethod("GET");
|
||||||
|
servletRequest.addHeader("If-Modified-Since", oneMinuteAgo);
|
||||||
|
|
||||||
|
request.checkNotModified(currentTime);
|
||||||
|
|
||||||
|
assertEquals(200, servletResponse.getStatus());
|
||||||
|
assertEquals(currentTime, servletResponse.getHeader("Last-Modified"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkNotModifiedETag() {
|
||||||
|
String eTag = "Foo";
|
||||||
|
servletRequest.setMethod("GET");
|
||||||
|
servletRequest.addHeader("If-None-Match", "\"" + eTag + "\"");
|
||||||
|
|
||||||
|
request.checkNotModified(eTag);
|
||||||
|
|
||||||
|
assertEquals(304, servletResponse.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkModifiedETagNonQuoted() {
|
||||||
|
String currentETag = "Foo";
|
||||||
|
String oldEtag = "Bar";
|
||||||
|
servletRequest.setMethod("GET");
|
||||||
|
servletRequest.addHeader("If-None-Match", "\"" + oldEtag + "\"");
|
||||||
|
|
||||||
|
request.checkNotModified(currentETag);
|
||||||
|
|
||||||
|
assertEquals(200, servletResponse.getStatus());
|
||||||
|
assertEquals("\"" + currentETag + "\"", servletResponse.getHeader("ETag"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkModifiedETagQuoted() {
|
||||||
|
String currentETag = "\"Foo\"";
|
||||||
|
String oldEtag = "Bar";
|
||||||
|
servletRequest.setMethod("GET");
|
||||||
|
servletRequest.addHeader("If-None-Match", oldEtag);
|
||||||
|
|
||||||
|
request.checkNotModified(currentETag);
|
||||||
|
|
||||||
|
assertEquals(200, servletResponse.getStatus());
|
||||||
|
assertEquals(currentETag, servletResponse.getHeader("ETag"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue