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.
This commit is contained in:
Yves Galante
2021-11-19 07:36:04 +01:00
committed by Oliver Drotbohm
parent 71ae98132c
commit c73d3816a6
5 changed files with 37 additions and 17 deletions

View File

@@ -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) {}
}
}

View File

@@ -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());
}

View File

@@ -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 {};
}

View File

@@ -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<String, Predicate<Class<?>>> 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<MediaType> 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.
*

View File

@@ -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 {};
}