From e70285331c3270d88fe47a576c13bcae795763a8 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 26 Jun 2014 09:43:37 +0200 Subject: [PATCH] DATAREST-333 - Added controller mappings for OPTIONS requests. The root resource, collection and item resources as well as the search and query method resources now expose a handler method to handle OPTIONS requests and return a response with the Allow header set to the HTTP methods appropriate to the resource requested. Added some additional methods for HEAD requests and a few integration tests for functionality that previously existed. Related ticket: DATAREST-330. --- .../AbstractRepositoryRestController.java | 8 ++ .../rest/webmvc/RepositoryController.java | 58 +++++++++++++-- .../webmvc/RepositoryEntityController.java | 36 ++++++++- .../webmvc/RepositorySearchController.java | 41 +++++++++++ .../rest/webmvc/RootResourceInformation.java | 3 +- .../RepositoryControllerIntegrationTests.java | 73 +++++++++++++++++++ ...itoryEntityControllerIntegrationTests.java | 37 +++++++++- ...itorySearchControllerIntegrationTests.java | 34 ++++++++- .../RootResourceInformationUnitTests.java | 7 +- .../data/rest/webmvc/WebTestUtils.java | 14 ++++ 10 files changed, 298 insertions(+), 13 deletions(-) create mode 100644 spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryControllerIntegrationTests.java 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 e2123235d..8c757a3de 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 @@ -45,6 +45,7 @@ import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.util.Assert; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @@ -61,7 +62,14 @@ class AbstractRepositoryRestController implements MessageSourceAware { private final PagedResourcesAssembler pagedResourcesAssembler; private MessageSourceAccessor messageSourceAccessor; + /** + * Creates a new {@link AbstractRepositoryRestController} for the given {@link PagedResourcesAssembler}. + * + * @param pagedResourcesAssembler must not be {@literal null}. + */ public AbstractRepositoryRestController(PagedResourcesAssembler pagedResourcesAssembler) { + + Assert.notNull(pagedResourcesAssembler, "PagedResourcesAssembler must not be null!"); this.pagedResourcesAssembler = pagedResourcesAssembler; } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryController.java index fa46d228b..007532ee7 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryController.java @@ -15,46 +15,94 @@ */ package org.springframework.data.rest.webmvc; +import java.util.Collections; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.repository.support.Repositories; import org.springframework.data.rest.core.mapping.ResourceMappings; import org.springframework.data.rest.core.mapping.ResourceMetadata; import org.springframework.data.web.PagedResourcesAssembler; import org.springframework.hateoas.EntityLinks; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.util.Assert; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; /** + * Controller for the root resource exposing links to the repository resources. + * * @author Jon Brisbin * @author Oliver Gierke */ @RepositoryRestController +@RequestMapping("/") public class RepositoryController extends AbstractRepositoryRestController { private final Repositories repositories; private final EntityLinks entityLinks; private final ResourceMappings mappings; + /** + * Creates a new {@link RepositoryController} for the given {@link PagedResourcesAssembler}, {@link Repositories}, + * {@link EntityLinks} and {@link ResourceMappings}. + * + * @param assembler must not be {@literal null}. + * @param repositories must not be {@literal null}. + * @param entityLinks must not be {@literal null}. + * @param mappings must not be {@literal null}. + */ @Autowired public RepositoryController(PagedResourcesAssembler assembler, Repositories repositories, EntityLinks entityLinks, ResourceMappings mappings) { super(assembler); + Assert.notNull(repositories, "Repositories must not be null!"); + Assert.notNull(entityLinks, "EntityLinks must not be null!"); + Assert.notNull(mappings, "ResourceMappings must not be null!"); + this.repositories = repositories; this.entityLinks = entityLinks; this.mappings = mappings; } + /** + * OPTIONS /. + * + * @return + * @since 2.2 + */ + @RequestMapping(method = RequestMethod.OPTIONS) + public HttpEntity optionsForRepositories() { + + HttpHeaders headers = new HttpHeaders(); + headers.setAllow(Collections.singleton(HttpMethod.GET)); + + return new ResponseEntity(headers, HttpStatus.OK); + } + + /** + * HEAD / + * + * @return + * @since 2.2 + */ + @RequestMapping(method = RequestMethod.HEAD) + public ResponseEntity headForRepositories() { + return new ResponseEntity(HttpStatus.NO_CONTENT); + } + /** * Lists all repositories exported by creating a link list pointing to resources exposing the repositories. * * @return */ - @ResponseBody - @RequestMapping(value = "/", method = RequestMethod.GET) - public RepositoryLinksResource listRepositories() { + @RequestMapping(method = RequestMethod.GET) + public HttpEntity listRepositories() { RepositoryLinksResource resource = new RepositoryLinksResource(); @@ -66,6 +114,6 @@ public class RepositoryController extends AbstractRepositoryRestController { } } - return resource; + return new ResponseEntity(resource, HttpStatus.OK); } } 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 41295d8f9..0c3c61310 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 @@ -101,12 +101,29 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem this.publisher = publisher; } + /** + * OPTIONS /{repository}. + * + * @param information + * @return + * @since 2.2 + */ + @RequestMapping(value = BASE_MAPPING, method = RequestMethod.OPTIONS) + public ResponseEntity optionsForCollectionResource(RootResourceInformation information) { + + HttpHeaders headers = new HttpHeaders(); + headers.setAllow(information.getSupportedMethods(ResourceType.COLLECTION)); + + return new ResponseEntity(headers, HttpStatus.OK); + } + /** * HEAD /{repository} * * @param resourceInformation * @return * @throws HttpRequestMethodNotSupportedException + * @since 2.2 */ @RequestMapping(value = BASE_MAPPING, method = RequestMethod.HEAD) public ResponseEntity headCollectionResource(RootResourceInformation resourceInformation) @@ -214,6 +231,22 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem return createAndReturn(payload.getContent(), resourceInformation.getInvoker(), assembler); } + /** + * OPTIONS /{repository}/{id} + * + * @param information + * @return + * @since 2.2 + */ + @RequestMapping(value = BASE_MAPPING + "/{id}", method = RequestMethod.OPTIONS) + public ResponseEntity optionsForItemResource(RootResourceInformation information) { + + HttpHeaders headers = new HttpHeaders(); + headers.setAllow(information.getSupportedMethods(ResourceType.ITEM)); + + return new ResponseEntity(headers, HttpStatus.OK); + } + /** * HEAD /{repsoitory}/{id} * @@ -221,9 +254,10 @@ class RepositoryEntityController extends AbstractRepositoryRestController implem * @param id * @return * @throws HttpRequestMethodNotSupportedException + * @since 2.2 */ @RequestMapping(value = BASE_MAPPING + "/{id}", method = RequestMethod.HEAD) - public ResponseEntity headItemResource(RootResourceInformation resourceInformation, @BackendId Serializable id) + public ResponseEntity headForItemResource(RootResourceInformation resourceInformation, @BackendId Serializable id) throws HttpRequestMethodNotSupportedException { if (getItemResource(resourceInformation, id) != null) { diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositorySearchController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositorySearchController.java index fd856538e..ccb3dbb52 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositorySearchController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositorySearchController.java @@ -20,6 +20,7 @@ import static org.springframework.data.rest.webmvc.ControllerUtils.*; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -38,6 +39,8 @@ import org.springframework.hateoas.Resource; import org.springframework.hateoas.ResourceSupport; import org.springframework.hateoas.Resources; import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; @@ -88,6 +91,24 @@ class RepositorySearchController extends AbstractRepositoryRestController { this.assembler = assembler; } + /** + * OPTIONS /{repository}/search. + * + * @param resourceInformation + * @return + * @since 2.2 + */ + @RequestMapping(value = BASE_MAPPING, method = RequestMethod.OPTIONS) + public HttpEntity optionsForSearches(RootResourceInformation resourceInformation) { + + verifySearchesExposed(resourceInformation); + + HttpHeaders headers = new HttpHeaders(); + headers.setAllow(Collections.singleton(HttpMethod.GET)); + + return new ResponseEntity(headers, HttpStatus.OK); + } + /** * HEAD /{repository}/search - Checks whether the search resource is present. * @@ -187,12 +208,32 @@ class RepositorySearchController extends AbstractRepositoryRestController { return new Resources>(EMPTY_RESOURCE_LIST, links); } + /** + * OPTIONS /{repository}/search/{search}. + * + * @param information + * @param search + * @return + * @since 2.2 + */ + @RequestMapping(value = BASE_MAPPING + "/{search}", method = RequestMethod.OPTIONS) + public ResponseEntity optionsForSearch(RootResourceInformation information, @PathVariable String search) { + + checkExecutability(information, search); + + HttpHeaders headers = new HttpHeaders(); + headers.setAllow(Collections.singleton(HttpMethod.GET)); + + return new ResponseEntity(headers, HttpStatus.OK); + } + /** * Handles a {@code HEAD} request for individual searches. * * @param information * @param search * @return + * @since 2.2 */ @RequestMapping(value = BASE_MAPPING + "/{search}", method = RequestMethod.HEAD) public ResponseEntity headForSearch(RootResourceInformation information, @PathVariable String search) { diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RootResourceInformation.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RootResourceInformation.java index 68837534f..d9592036f 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RootResourceInformation.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RootResourceInformation.java @@ -80,7 +80,7 @@ public class RootResourceInformation { * @param resourcType must not be {@literal null}. * @return */ - public Collection getSupportedMethods(ResourceType resourcType) { + public Set getSupportedMethods(ResourceType resourcType) { Assert.notNull(resourcType, "Resource type must not be null!"); @@ -89,6 +89,7 @@ public class RootResourceInformation { } Set methods = new HashSet(); + methods.add(HttpMethod.OPTIONS); switch (resourcType) { case COLLECTION: diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryControllerIntegrationTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryControllerIntegrationTests.java new file mode 100644 index 000000000..2ee4ce3fc --- /dev/null +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryControllerIntegrationTests.java @@ -0,0 +1,73 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.rest.webmvc; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; +import static org.springframework.data.rest.webmvc.WebTestUtils.*; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.rest.webmvc.jpa.JpaRepositoryConfig; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.transaction.annotation.Transactional; + +/** + * Integration tests for {@link RepositoryController}. + * + * @author Oliver Gierke + */ +@ContextConfiguration(classes = JpaRepositoryConfig.class) +@Transactional +public class RepositoryControllerIntegrationTests extends AbstractControllerIntegrationTests { + + @Autowired RepositoryController controller; + + /** + * @see DATAREST-333 + */ + @Test + public void rootResourceExposesGetOnly() { + + HttpEntity response = controller.optionsForRepositories(); + assertAllowHeaders(response, HttpMethod.GET); + } + + /** + * @see DATAREST-333, DATAREST-330 + */ + @Test + public void headRequestReturnsNoContent() { + assertThat(controller.headForRepositories().getStatusCode(), is(HttpStatus.NO_CONTENT)); + } + + @Test + public void exposesLinksToRepositories() { + + RepositoryLinksResource resource = controller.listRepositories().getBody(); + + assertThat(resource.getLinks(), hasSize(5)); + + assertThat(resource.hasLink("people"), is(true)); + assertThat(resource.hasLink("orders"), is(true)); + assertThat(resource.hasLink("addresses"), is(true)); + assertThat(resource.hasLink("books"), is(true)); + assertThat(resource.hasLink("authors"), is(true)); + } +} diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryEntityControllerIntegrationTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryEntityControllerIntegrationTests.java index 8501042b5..ef34a333b 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryEntityControllerIntegrationTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryEntityControllerIntegrationTests.java @@ -17,6 +17,8 @@ package org.springframework.data.rest.webmvc; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import static org.springframework.data.rest.webmvc.WebTestUtils.*; +import static org.springframework.http.HttpMethod.*; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -28,6 +30,7 @@ import org.springframework.data.rest.webmvc.jpa.CreditCard; 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.http.HttpEntity; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.test.context.ContextConfiguration; @@ -113,7 +116,7 @@ public class RepositoryEntityControllerIntegrationTests extends AbstractControll Address address = repository.save(new Address()); - ResponseEntity entity = controller.headItemResource(getResourceInformation(Address.class), address.id); + ResponseEntity entity = controller.headForItemResource(getResourceInformation(Address.class), address.id); assertThat(entity.getStatusCode(), is(HttpStatus.NO_CONTENT)); } @@ -122,6 +125,36 @@ public class RepositoryEntityControllerIntegrationTests extends AbstractControll */ @Test(expected = ResourceNotFoundException.class) public void doesNotExposeHeadForItemResourceIfNotExisting() throws Exception { - controller.headItemResource(getResourceInformation(CreditCard.class), 1L); + controller.headForItemResource(getResourceInformation(CreditCard.class), 1L); + } + + /** + * @see DATAREST-333 + */ + @Test + public void doesNotExposeMethodsForOptionsIfNotHttpMethodsSupportedForCollectionResource() { + + HttpEntity response = controller.optionsForCollectionResource(getResourceInformation(Address.class)); + assertAllowHeaders(response, OPTIONS); + } + + /** + * @see DATAREST-333 + */ + @Test + public void exposesSupportedHttpMethodsInAllowHeaderForOptionsRequestToCollectionResource() { + + HttpEntity response = controller.optionsForCollectionResource(getResourceInformation(Person.class)); + assertAllowHeaders(response, GET, POST, HEAD, OPTIONS); + } + + /** + * @see DATAREST-333 + */ + @Test + public void exposesSupportedHttpMethodsInAllowHeaderForOptionsRequestToItemResource() { + + HttpEntity response = controller.optionsForItemResource(getResourceInformation(Person.class)); + assertAllowHeaders(response, GET, PUT, PATCH, DELETE, HEAD, OPTIONS); } } diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositorySearchControllerIntegrationTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositorySearchControllerIntegrationTests.java index e01426760..e83dcad1c 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositorySearchControllerIntegrationTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositorySearchControllerIntegrationTests.java @@ -15,14 +15,16 @@ */ package org.springframework.data.rest.webmvc; -import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import static org.springframework.data.rest.webmvc.WebTestUtils.*; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.rest.core.mapping.ResourceMetadata; import org.springframework.data.rest.webmvc.ResourceTester.HasSelfLink; +import org.springframework.data.rest.webmvc.jpa.Address; import org.springframework.data.rest.webmvc.jpa.CreditCard; import org.springframework.data.rest.webmvc.jpa.JpaRepositoryConfig; import org.springframework.data.rest.webmvc.jpa.Order; @@ -30,6 +32,8 @@ import org.springframework.data.rest.webmvc.jpa.Person; import org.springframework.data.rest.webmvc.jpa.TestDataPopulator; import org.springframework.hateoas.PagedResources; import org.springframework.hateoas.ResourceSupport; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.test.context.ContextConfiguration; import org.springframework.transaction.annotation.Transactional; @@ -135,4 +139,32 @@ public class RepositorySearchControllerIntegrationTests extends AbstractControll public void doesNotExposeHeadForInvalidQueryMethodResource() { controller.headForSearch(getResourceInformation(Person.class), "foobar"); } + + /** + * @see DATAREST-333 + */ + @Test + public void searchResourceSupportsGetOnly() { + assertAllowHeaders(controller.optionsForSearches(getResourceInformation(Person.class)), HttpMethod.GET); + } + + /** + * @see DATAREST-333 + */ + @Test(expected = ResourceNotFoundException.class) + public void returns404ForOptionsForRepositoryWithoutSearches() { + controller.optionsForSearches(getResourceInformation(Address.class)); + } + + /** + * @see DATAREST-333 + */ + @Test + public void queryMethodResourceSupportsGetOnly() { + + RootResourceInformation resourceInformation = getResourceInformation(Person.class); + HttpEntity response = controller.optionsForSearch(resourceInformation, "firstname"); + + assertAllowHeaders(response, HttpMethod.GET); + } } diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RootResourceInformationUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RootResourceInformationUnitTests.java index 0ad386b5f..0f644c977 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RootResourceInformationUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RootResourceInformationUnitTests.java @@ -21,6 +21,7 @@ import static org.mockito.Mockito.*; import static org.springframework.data.rest.webmvc.ResourceType.*; import static org.springframework.http.HttpMethod.*; +import org.atteo.evo.inflector.English; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -57,15 +58,15 @@ public class RootResourceInformationUnitTests { } /** - * @see DATAREST-217 + * @see DATAREST-217, DATAREST-330 */ @Test public void defaultsSupportedHttpMethodsForItemResource() { - assertThat(information.getSupportedMethods(ResourceType.ITEM), hasItems(GET, PUT, PATCH, DELETE)); + assertThat(information.getSupportedMethods(ResourceType.ITEM), hasItems(GET, PUT, PATCH, DELETE, OPTIONS)); assertThat(information.getSupportedMethods(ResourceType.ITEM), not(hasItems(POST))); - assertThat(information.getSupportedMethods(COLLECTION), hasItems(GET, POST)); + assertThat(information.getSupportedMethods(COLLECTION), hasItems(GET, POST, OPTIONS)); assertThat(information.getSupportedMethods(COLLECTION), not(hasItems(PUT, PATCH, DELETE))); } diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/WebTestUtils.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/WebTestUtils.java index c56670102..f70ea863a 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/WebTestUtils.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/WebTestUtils.java @@ -15,6 +15,12 @@ */ package org.springframework.data.rest.webmvc; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -35,4 +41,12 @@ public class WebTestUtils { ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request); RequestContextHolder.setRequestAttributes(requestAttributes); } + + public static void assertAllowHeaders(HttpEntity response, HttpMethod... methods) { + + HttpHeaders headers = response.getHeaders(); + + assertThat(headers.getAllow(), hasSize(methods.length)); + assertThat(headers.getAllow(), hasItems(methods)); + } }