From c73d3816a6b33d8d0a878217e4cb57ce86a4e97d Mon Sep 17 00:00:00 2001 From: Yves Galante Date: Fri, 19 Nov 2021 07:36:04 +0100 Subject: [PATCH] Add support for controller base path on BasePathAwareController / RepositoryRestController. This commit introduces support for a common base path shared amongst all handler methods of a controller annotated with @BasePathAwareController / @RepositoryRestController. Related ticket: #2087 Original pull request: #2088. --- .../rest/webmvc/jpa/JpaRepositoryConfig.java | 4 +-- .../data/rest/webmvc/jpa/JpaWebTests.java | 7 +++-- .../rest/webmvc/BasePathAwareController.java | 6 ++++ .../webmvc/BasePathAwareHandlerMapping.java | 31 +++++++++++-------- .../rest/webmvc/RepositoryRestController.java | 6 ++++ 5 files changed, 37 insertions(+), 17 deletions(-) diff --git a/spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaRepositoryConfig.java b/spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaRepositoryConfig.java index 73a37eb79..2d4589796 100644 --- a/spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaRepositoryConfig.java +++ b/spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaRepositoryConfig.java @@ -58,10 +58,10 @@ public class JpaRepositoryConfig extends JpaInfrastructureConfig { void someMethod(@PathVariable String id) {} } - @RepositoryRestController + @RepositoryRestController(path = {"orders", "orders/v2"}) static class OrdersJsonController { - @RequestMapping(value = "/orders/search/sort", method = RequestMethod.POST, produces = "application/hal+json") + @RequestMapping(value = {"/search/sort","/search/sorted"}, method = RequestMethod.POST, produces = "application/hal+json") void someMethodWithArgs(Sort sort, Pageable pageable, DefaultedPageable defaultedPageable) {} } } diff --git a/spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaWebTests.java b/spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaWebTests.java index 65ac9d62d..0de29de12 100755 --- a/spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaWebTests.java +++ b/spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/jpa/JpaWebTests.java @@ -702,10 +702,13 @@ public class JpaWebTests extends CommonWebTests { andExpect(client.hasLinkWithRel(IanaLinkRelations.SELF)); } - @Test // DATAREST-910 + @Test // DATAREST-910 DATAREST-2088 void callUnmappedCustomRepositoryController() throws Exception { - + mvc.perform(post("/orders/v3/search/sort")).andExpect(status().isNotFound()); mvc.perform(post("/orders/search/sort")).andExpect(status().isOk()); + mvc.perform(post("/orders/search/sorted")).andExpect(status().isOk()); + mvc.perform(post("/orders/v2/search/sort")).andExpect(status().isOk()); + mvc.perform(post("/orders/v2/search/sorted")).andExpect(status().isOk()); mvc.perform(post("/orders/search/sort?sort=type&page=1&size=10")).andExpect(status().isOk()); } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/BasePathAwareController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/BasePathAwareController.java index a59b2ce12..f1c055feb 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/BasePathAwareController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/BasePathAwareController.java @@ -21,6 +21,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.springframework.core.annotation.AliasFor; import org.springframework.stereotype.Component; /** @@ -34,4 +35,9 @@ import org.springframework.stereotype.Component; @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE }) public @interface BasePathAwareController { + @AliasFor("path") + String[] value() default {}; + + @AliasFor("value") + String[] path() default {}; } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/BasePathAwareHandlerMapping.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/BasePathAwareHandlerMapping.java index c006fca05..647f77e84 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/BasePathAwareHandlerMapping.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/BasePathAwareHandlerMapping.java @@ -19,11 +19,8 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.function.Predicate; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; @@ -41,6 +38,8 @@ import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import static org.springframework.core.annotation.AnnotatedElementUtils.*; + /** * A {@link RequestMappingHandlerMapping} that augments the request mappings * @@ -51,6 +50,8 @@ public class BasePathAwareHandlerMapping extends RequestMappingHandlerMapping { private static final String AT_REQUEST_MAPPING_ON_TYPE = "Spring Data REST controller %s must not use @RequestMapping on class level as this would cause double registration with Spring MVC!"; private final RepositoryRestConfiguration configuration; + private final String baseUri; + /** * Creates a new {@link BasePathAwareHandlerMapping} using the given {@link RepositoryRestConfiguration}. * @@ -61,16 +62,7 @@ public class BasePathAwareHandlerMapping extends RequestMappingHandlerMapping { Assert.notNull(configuration, "RepositoryRestConfiguration must not be null!"); this.configuration = configuration; - - String baseUri = configuration.getBasePath().toString(); - - if (StringUtils.hasText(baseUri)) { - - Map>> prefixes = new HashMap<>(); - prefixes.put(baseUri, it -> true); - - this.setPathPrefixes(prefixes); - } + this.baseUri = configuration.getBasePath().toString(); } /* @@ -128,11 +120,24 @@ public class BasePathAwareHandlerMapping extends RequestMappingHandlerMapping { ProducesRequestCondition producesCondition = customize(info.getProducesCondition()); Set mediaTypes = producesCondition.getProducibleMediaTypes(); + BasePathAwareController mergedAnnotation = findMergedAnnotation(handlerType, BasePathAwareController.class); + if (mergedAnnotation != null) { + info = appendPathPrefix(info, mergedAnnotation.value()); + } + info = appendPathPrefix(info, new String[]{this.baseUri}); return info.mutate() .produces(mediaTypes.stream().map(MediaType::toString).toArray(String[]::new)) .build(); } + private RequestMappingInfo appendPathPrefix(RequestMappingInfo info, String[] pathPrefix) { + if (pathPrefix.length > 0) { + String[] paths = this.resolveEmbeddedValuesInPatterns(pathPrefix); + return info.mutate().paths(paths).build().combine(info); + } + return info; + } + /** * Customize the given {@link ProducesRequestCondition}. Default implementation returns the condition as is. * diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestController.java index bcdb03f46..2058c0e3f 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestController.java @@ -21,6 +21,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.springframework.core.annotation.AliasFor; import org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerMapping; @@ -46,4 +47,9 @@ import org.springframework.web.servlet.HandlerMapping; @Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE }) @BasePathAwareController public @interface RepositoryRestController { + @AliasFor("path") + String[] value() default {}; + + @AliasFor("value") + String[] path() default {}; }