提交 7cc3f499 编写于 作者: A Arjen Poutsma

SPR-7695 - Add ETag version of WebRequest.checkNotModified()

上级 095a36e8
...@@ -177,6 +177,14 @@ public class PortletWebRequest extends PortletRequestAttributes implements Nativ ...@@ -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 ...@@ -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; ...@@ -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 ...@@ -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 ...@@ -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 { ...@@ -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 { ...@@ -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 @@ ...@@ -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; ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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"));
}
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册