DATAREST-34 - PUT and POST request now consider Accept header by default.

By default, whether to return response bodies for PUT and POST is determined by the presence of an Accept header, unless explicitly activated or deactivated in RepositoryRestConfiguration.

Original pull request: #167.
This commit is contained in:
Jeremy Rickard
2015-03-09 20:17:13 -06:00
committed by Oliver Gierke
parent 4f8298c711
commit f3c74ac9db
4 changed files with 157 additions and 28 deletions

View File

@@ -63,6 +63,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@@ -81,6 +82,8 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
RestMediaTypes.JSON_PATCH_JSON.toString(), //
MediaType.APPLICATION_JSON_VALUE);
private static final String ACCEPT_HEADER = "Accept";
private final RepositoryEntityLinks entityLinks;
private final RepositoryRestConfiguration config;
private final ConversionService conversionService;
@@ -227,18 +230,23 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
*
* @param resourceInformation
* @param payload
* @param assembler
* @param acceptHeader
* @return
* @throws HttpRequestMethodNotSupportedException
*/
@ResponseBody
@RequestMapping(value = BASE_MAPPING, method = RequestMethod.POST)
public ResponseEntity<ResourceSupport> postCollectionResource(RootResourceInformation resourceInformation,
PersistentEntityResource payload, PersistentEntityResourceAssembler assembler)
PersistentEntityResource payload, PersistentEntityResourceAssembler assembler,
@RequestHeader(value= ACCEPT_HEADER, required = false) String acceptHeader)
throws HttpRequestMethodNotSupportedException {
resourceInformation.verifySupportedMethod(HttpMethod.POST, ResourceType.COLLECTION);
return createAndReturn(payload.getContent(), resourceInformation.getInvoker(), assembler);
boolean acceptHeaderPresent = acceptHeader != null;
return createAndReturn(payload.getContent(), resourceInformation.getInvoker(), assembler, acceptHeaderPresent);
}
/**
@@ -312,17 +320,21 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
/**
* <code>PUT /{repository}/{id}</code> - Updates an existing entity or creates one at exactly that place.
*
* @param eTagMatch
* @param resourceInformation
* @param payload
* @param id
* @param assembler
* @param eTag
* @param acceptHeader
* @return
* @throws HttpRequestMethodNotSupportedException
*/
@RequestMapping(value = BASE_MAPPING + "/{id}", method = RequestMethod.PUT)
public ResponseEntity<? extends ResourceSupport> putItemResource(RootResourceInformation resourceInformation,
PersistentEntityResource payload, @BackendId Serializable id, PersistentEntityResourceAssembler assembler,
ETag eTag) throws HttpRequestMethodNotSupportedException {
ETag eTag, @RequestHeader(value=ACCEPT_HEADER, required = false) String acceptHeader)
throws HttpRequestMethodNotSupportedException {
resourceInformation.verifySupportedMethod(HttpMethod.PUT, ResourceType.ITEM);
@@ -338,8 +350,10 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
eTag.verify(resourceInformation.getPersistentEntity(), domainObject);
return domainObject == null ? createAndReturn(objectToSave, invoker, assembler) : saveAndReturn(objectToSave,
invoker, PUT, assembler);
boolean acceptHeaderPresent = acceptHeader != null;
return domainObject == null ? createAndReturn(objectToSave, invoker, assembler, acceptHeaderPresent)
: saveAndReturn(objectToSave, invoker, PUT, assembler, acceptHeaderPresent);
}
/**
@@ -349,7 +363,8 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
* @param payload
* @param id
* @param assembler
* @param eTag
* @param eTag,
* @param acceptHeader
* @return
* @throws HttpRequestMethodNotSupportedException
* @throws ResourceNotFoundException
@@ -358,7 +373,8 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
@RequestMapping(value = BASE_MAPPING + "/{id}", method = RequestMethod.PATCH)
public ResponseEntity<ResourceSupport> patchItemResource(RootResourceInformation resourceInformation,
PersistentEntityResource payload, @BackendId Serializable id, PersistentEntityResourceAssembler assembler,
ETag eTag) throws HttpRequestMethodNotSupportedException, ResourceNotFoundException {
ETag eTag,@RequestHeader(value=ACCEPT_HEADER, required = false) String acceptHeader )
throws HttpRequestMethodNotSupportedException, ResourceNotFoundException {
resourceInformation.verifySupportedMethod(HttpMethod.PATCH, ResourceType.ITEM);
@@ -370,7 +386,9 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
eTag.verify(resourceInformation.getPersistentEntity(), domainObject);
return saveAndReturn(payload.getContent(), resourceInformation.getInvoker(), PATCH, assembler);
boolean acceptHeaderPresent = acceptHeader != null;
return saveAndReturn(payload.getContent(), resourceInformation.getInvoker(), PATCH, assembler, acceptHeaderPresent);
}
/**
@@ -415,7 +433,7 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
* @return
*/
private ResponseEntity<ResourceSupport> saveAndReturn(Object domainObject, RepositoryInvoker invoker,
HttpMethod httpMethod, PersistentEntityResourceAssembler assembler) {
HttpMethod httpMethod, PersistentEntityResourceAssembler assembler, boolean acceptHeaderPresent) {
publisher.publishEvent(new BeforeSaveEvent(domainObject));
Object obj = invoker.invokeSave(domainObject);
@@ -428,7 +446,10 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
addLocationHeader(headers, assembler, obj);
}
if (config.isReturnBodyOnUpdate()) {
boolean returnBodyOnUpdate = (config.isReturnBodyOnUpdate() == null && acceptHeaderPresent)
|| Boolean.TRUE.equals(config.isReturnBodyOnUpdate());
if (returnBodyOnUpdate) {
return ControllerUtils.toResponseEntity(HttpStatus.OK, headers, resource);
} else {
return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT, headers);
@@ -443,13 +464,17 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem
* @return
*/
private ResponseEntity<ResourceSupport> createAndReturn(Object domainObject, RepositoryInvoker invoker,
PersistentEntityResourceAssembler assembler) {
PersistentEntityResourceAssembler assembler, boolean acceptHeaderPresent) {
publisher.publishEvent(new BeforeCreateEvent(domainObject));
Object savedObject = invoker.invokeSave(domainObject);
publisher.publishEvent(new AfterCreateEvent(savedObject));
PersistentEntityResource resource = config.isReturnBodyOnCreate() ? assembler.toFullResource(savedObject) : null;
boolean returnBodyOnCreate = (config.isReturnBodyOnCreate() == null && acceptHeaderPresent)
|| Boolean.TRUE.equals(config.isReturnBodyOnCreate());
PersistentEntityResource resource = returnBodyOnCreate ? assembler.toFullResource(savedObject) : null;
HttpHeaders headers = prepareHeaders(resource);
addLocationHeader(headers, assembler, savedObject);

View File

@@ -22,6 +22,7 @@ import static org.springframework.http.HttpMethod.*;
import java.util.List;
import org.junit.After;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mapping.context.PersistentEntities;
@@ -33,6 +34,7 @@ import org.springframework.data.rest.webmvc.jpa.JpaRepositoryConfig;
import org.springframework.data.rest.webmvc.jpa.Order;
import org.springframework.data.rest.webmvc.jpa.Person;
import org.springframework.data.rest.webmvc.support.ETag;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@@ -46,6 +48,7 @@ import org.springframework.web.HttpRequestMethodNotSupportedException;
*
* @author Oliver Gierke
*/
@SuppressWarnings("ALL")
@ContextConfiguration(classes = JpaRepositoryConfig.class)
@Transactional
public class RepositoryEntityControllerIntegrationTests extends AbstractControllerIntegrationTests {
@@ -76,7 +79,7 @@ public class RepositoryEntityControllerIntegrationTests extends AbstractControll
RootResourceInformation request = getResourceInformation(Address.class);
controller.postCollectionResource(request, null, null);
controller.postCollectionResource(request, null, null, MediaType.APPLICATION_JSON_VALUE);
}
/**
@@ -91,7 +94,7 @@ public class RepositoryEntityControllerIntegrationTests extends AbstractControll
entities.getPersistentEntity(Order.class)).build();
ResponseEntity<?> entity = controller.putItemResource(information, persistentEntityResource, 1L, assembler,
ETag.NO_ETAG);
ETag.NO_ETAG, MediaType.APPLICATION_JSON_VALUE);
assertThat(entity.getHeaders().getLocation().toString(), not(endsWith("{?projection}")));
}
@@ -181,4 +184,99 @@ public class RepositoryEntityControllerIntegrationTests extends AbstractControll
RestMediaTypes.MERGE_PATCH_JSON.toString(), //
MediaType.APPLICATION_JSON_VALUE));
}
/**
* @see DATAREST-34
*/
@Test
public void verifyAcceptHeaderCanControlBodyReturnOnPutItemResource() throws HttpRequestMethodNotSupportedException {
RootResourceInformation request = getResourceInformation(Order.class);
PersistentEntityResource persistentEntityResource = PersistentEntityResource.build(new Order(new Person()),
entities.getPersistentEntity(Order.class)).build();
configuration.setReturnBodyOnCreate(Boolean.FALSE);
configuration.setReturnBodyOnUpdate(Boolean.FALSE);
ResponseEntity<?> response = controller.putItemResource(request, persistentEntityResource, 1L, assembler,
ETag.NO_ETAG, MediaType.APPLICATION_JSON_VALUE);
assert(!response.hasBody());
configuration.setReturnBodyOnCreate(Boolean.TRUE);
configuration.setReturnBodyOnUpdate(Boolean.TRUE);
response = controller.putItemResource(request, persistentEntityResource, 1L, assembler,
ETag.NO_ETAG, MediaType.APPLICATION_JSON_VALUE);
configuration.setReturnBodyOnCreate(Boolean.FALSE);
configuration.setReturnBodyOnUpdate(Boolean.FALSE);
response = controller.putItemResource(request, persistentEntityResource, 1L, assembler,
ETag.NO_ETAG, null);
assert(!response.hasBody());
configuration.setReturnBodyOnCreate(null);
configuration.setReturnBodyOnUpdate(null);
response = controller.putItemResource(request, persistentEntityResource, 1L, assembler,
ETag.NO_ETAG, null);
assert(!response.hasBody());
configuration.setReturnBodyOnCreate(null);
configuration.setReturnBodyOnUpdate(null);
response = controller.putItemResource(request, persistentEntityResource, 1L, assembler,
ETag.NO_ETAG, MediaType.APPLICATION_JSON_VALUE);
assert(response.hasBody());
}
/**
* @see DATAREST-34
*/
@Test
public void verifyAcceptHeaderCanControlBodyReturnPostCollectionResource() throws HttpRequestMethodNotSupportedException {
RootResourceInformation request = getResourceInformation(Order.class);
PersistentEntityResource persistentEntityResource = PersistentEntityResource.build(new Order(new Person()),
entities.getPersistentEntity(Order.class)).build();
configuration.setReturnBodyOnCreate(null);
ResponseEntity<ResourceSupport> response =
controller.postCollectionResource(request, persistentEntityResource, assembler, MediaType.APPLICATION_JSON_VALUE);
assert(response.hasBody());
response =
controller.postCollectionResource(request, persistentEntityResource, assembler, null);
assert(!response.hasBody());
configuration.setReturnBodyOnCreate(Boolean.FALSE);
response =
controller.postCollectionResource(request, persistentEntityResource, assembler, MediaType.APPLICATION_JSON_VALUE);
assert(!response.hasBody());
configuration.setReturnBodyOnCreate(Boolean.TRUE);
response =
controller.postCollectionResource(request, persistentEntityResource, assembler, null);
assert(response.hasBody());
}
@After
public void cleanUp() {
configuration.setReturnBodyOnCreate(Boolean.FALSE);
configuration.setReturnBodyOnUpdate(Boolean.FALSE);
}
}