From bb933237b0f189d422f088503763f5da8881af8a Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Sun, 12 Apr 2015 18:18:54 +0200 Subject: [PATCH] DATAREST-506 - NotModified responses now return headers, too. Responses for item resources that result in 304 Not Modifier now also return the headers they'd return if a 200 Ok would've been returned. --- .../AbstractRepositoryRestController.java | 21 ++++++++++++------ .../webmvc/RepositoryEntityController.java | 8 ++++--- .../data/rest/webmvc/support/ETag.java | 22 +++++++++---------- .../rest/webmvc/mongodb/MongoWebTests.java | 11 ++++++---- 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/AbstractRepositoryRestController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/AbstractRepositoryRestController.java index ab695aeeb..7224f63d2 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/AbstractRepositoryRestController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/AbstractRepositoryRestController.java @@ -24,6 +24,7 @@ import java.util.List; import org.springframework.data.auditing.AuditableBeanWrapper; import org.springframework.data.auditing.AuditableBeanWrapperFactory; import org.springframework.data.domain.Page; +import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.rest.core.mapping.ResourceMetadata; import org.springframework.data.rest.webmvc.support.ETag; import org.springframework.data.web.PagedResourcesAssembler; @@ -132,18 +133,24 @@ class AbstractRepositoryRestController { * @return */ protected HttpHeaders prepareHeaders(PersistentEntityResource resource) { + return resource == null ? new HttpHeaders() : prepareHeaders(resource.getPersistentEntity(), resource.getContent()); + } - HttpHeaders headers = new HttpHeaders(); - - if (resource == null) { - return headers; - } + /** + * Retruns the default headers to be returned for the given {@link PersistentEntity} and value. Will set {@link ETag} + * and {@code Last-Modified} headers if applicable. + * + * @param entity must not be {@literal null}. + * @param value must not be {@literal null}. + * @return + */ + protected HttpHeaders prepareHeaders(PersistentEntity entity, Object value) { // Add ETag - headers = ETag.from(resource).addTo(headers); + HttpHeaders headers = ETag.from(entity, value).addTo(new HttpHeaders()); // Add Last-Modified - AuditableBeanWrapper wrapper = getAuditableBeanWrapper(resource.getContent()); + AuditableBeanWrapper wrapper = getAuditableBeanWrapper(value); if (wrapper == null) { return headers; diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryEntityController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryEntityController.java index 2c439f852..f526e23b8 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryEntityController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryEntityController.java @@ -31,6 +31,7 @@ import org.springframework.core.convert.ConversionService; import org.springframework.data.auditing.AuditableBeanWrapper; import org.springframework.data.auditing.AuditableBeanWrapperFactory; import org.springframework.data.domain.Sort; +import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.model.ConvertingPropertyAccessor; import org.springframework.data.repository.support.Repositories; @@ -319,9 +320,10 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem List ifNoneMatch = headers.getIfNoneMatch(); ETag eTag = ifNoneMatch.isEmpty() ? ETag.NO_ETAG : ETag.from(ifNoneMatch.get(0)); + PersistentEntity entity = resourceInformation.getPersistentEntity(); - if (eTag.matches(resourceInformation.getPersistentEntity(), domainObj)) { - return new ResponseEntity>(HttpStatus.NOT_MODIFIED); + if (eTag.matches(entity, domainObj)) { + return new ResponseEntity>(prepareHeaders(entity, domainObj), HttpStatus.NOT_MODIFIED); } // Check last modification for If-Modfied-Since @@ -332,7 +334,7 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem long current = wrapper.getLastModifiedDate().getTimeInMillis() / 1000 * 1000; if (current <= headers.getIfModifiedSince()) { - return new ResponseEntity>(HttpStatus.NOT_MODIFIED); + return new ResponseEntity>(prepareHeaders(entity, domainObj), HttpStatus.NOT_MODIFIED); } } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/ETag.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/ETag.java index de211824b..a48f786ee 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/ETag.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/ETag.java @@ -65,6 +65,17 @@ public final class ETag { return resource == null ? NO_ETAG : from(resource.getPersistentEntity(), resource.getContent()); } + /** + * Creates a new {@link ETag} from the given {@link PersistentEntity} and target bean. + * + * @param entity must not be {@literal null}. + * @param bean must not be {@literal null}. + * @return + */ + public static ETag from(PersistentEntity entity, Object bean) { + return from(getVersionInformation(entity, bean)); + } + /** * Verifies the ETag to be created for the given target bean with the current one and raises a * {@link ETagDoesntMatchException} in case they don't match. @@ -160,17 +171,6 @@ public final class ETag { return value.hashCode(); } - /** - * Creates a new {@link ETag} from the given {@link PersistentEntity} and target bean. - * - * @param entity - * @param bean - * @return - */ - private static ETag from(PersistentEntity entity, Object bean) { - return from(getVersionInformation(entity, bean)); - } - /** * Returns the quoted version property of a domain object, returns null if it doesn't contains the property * diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mongodb/MongoWebTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mongodb/MongoWebTests.java index 90f18c55e..e4a2906ca 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mongodb/MongoWebTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mongodb/MongoWebTests.java @@ -17,6 +17,7 @@ package org.springframework.data.rest.webmvc.mongodb; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import static org.springframework.http.HttpHeaders.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @@ -292,10 +293,12 @@ public class MongoWebTests extends CommonWebTests { Link receiptLink = client.getDiscoverer(response).findLinkWithRel("self", response.getContentAsString()); - mvc.perform(get(receiptLink.getHref()).header("If-Modified-Since", response.getHeader("Last-Modified"))).// - andExpect(status().isNotModified()); + mvc.perform(get(receiptLink.getHref()).header(IF_MODIFIED_SINCE, response.getHeader(LAST_MODIFIED))).// + andExpect(status().isNotModified()).// + andExpect(header().string(ETAG, is(notNullValue()))); - mvc.perform(get(receiptLink.getHref()).header("If-None-Match", response.getHeader("ETag"))).// - andExpect(status().isNotModified()); + mvc.perform(get(receiptLink.getHref()).header(IF_NONE_MATCH, response.getHeader(ETAG))).// + andExpect(status().isNotModified()).// + andExpect(header().string(ETAG, is(notNullValue()))); } }