MvcUriComponentsBuilder is aware of path prefixes
Issue: SPR-16336
This commit is contained in:
@@ -174,8 +174,15 @@ public class MvcUriComponentsBuilder {
|
||||
Class<?> controllerType) {
|
||||
|
||||
builder = getBaseUrlToUse(builder);
|
||||
|
||||
// Externally configured prefix via PathConfigurer..
|
||||
String prefix = getPathPrefix(controllerType);
|
||||
builder.path(prefix);
|
||||
|
||||
String mapping = getClassMapping(controllerType);
|
||||
return builder.path(mapping);
|
||||
builder.path(mapping);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -526,15 +533,21 @@ public class MvcUriComponentsBuilder {
|
||||
}
|
||||
|
||||
|
||||
private static UriComponentsBuilder fromMethodInternal(@Nullable UriComponentsBuilder baseUrl,
|
||||
private static UriComponentsBuilder fromMethodInternal(@Nullable UriComponentsBuilder builder,
|
||||
Class<?> controllerType, Method method, Object... args) {
|
||||
|
||||
baseUrl = getBaseUrlToUse(baseUrl);
|
||||
builder = getBaseUrlToUse(builder);
|
||||
|
||||
// Externally configured prefix via PathConfigurer..
|
||||
String prefix = getPathPrefix(controllerType);
|
||||
builder.path(prefix);
|
||||
|
||||
String typePath = getClassMapping(controllerType);
|
||||
String methodPath = getMethodMapping(method);
|
||||
String path = pathMatcher.combine(typePath, methodPath);
|
||||
baseUrl.path(path);
|
||||
UriComponents uriComponents = applyContributors(baseUrl, method, args);
|
||||
builder.path(path);
|
||||
|
||||
UriComponents uriComponents = applyContributors(builder, method, args);
|
||||
return UriComponentsBuilder.newInstance().uriComponents(uriComponents);
|
||||
}
|
||||
|
||||
@@ -544,6 +557,22 @@ public class MvcUriComponentsBuilder {
|
||||
baseUrl.cloneBuilder();
|
||||
}
|
||||
|
||||
private static String getPathPrefix(Class<?> controllerType) {
|
||||
WebApplicationContext wac = getWebApplicationContext();
|
||||
if (wac != null) {
|
||||
Map<String, RequestMappingHandlerMapping> map = wac.getBeansOfType(RequestMappingHandlerMapping.class);
|
||||
for (RequestMappingHandlerMapping mapping : map.values()) {
|
||||
if (mapping.isHandler(controllerType)) {
|
||||
String prefix = mapping.getPathPrefix(controllerType);
|
||||
if (prefix != null) {
|
||||
return prefix;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static String getClassMapping(Class<?> controllerType) {
|
||||
Assert.notNull(controllerType, "'controllerType' must not be null");
|
||||
RequestMapping mapping = AnnotatedElementUtils.findMergedAnnotation(controllerType, RequestMapping.class);
|
||||
|
||||
@@ -32,7 +32,6 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
import org.springframework.web.accept.ContentNegotiationManager;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
@@ -67,7 +66,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||
|
||||
private boolean useTrailingSlashMatch = true;
|
||||
|
||||
private final Map<String, Predicate<Class<?>>> pathPrefixes = new LinkedHashMap<>();
|
||||
private Map<String, Predicate<Class<?>>> pathPrefixes = new LinkedHashMap<>();
|
||||
|
||||
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
|
||||
|
||||
@@ -120,10 +119,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||
* @since 5.1
|
||||
*/
|
||||
public void setPathPrefixes(Map<String, Predicate<Class<?>>> prefixes) {
|
||||
this.pathPrefixes.clear();
|
||||
prefixes.entrySet().stream()
|
||||
.filter(entry -> StringUtils.hasText(entry.getKey()))
|
||||
.forEach(entry -> this.pathPrefixes.put(entry.getKey(), entry.getValue()));
|
||||
this.pathPrefixes = Collections.unmodifiableMap(new LinkedHashMap<>(prefixes));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,7 +176,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||
* @since 5.1
|
||||
*/
|
||||
public Map<String, Predicate<Class<?>>> getPathPrefixes() {
|
||||
return Collections.unmodifiableMap(this.pathPrefixes);
|
||||
return this.pathPrefixes;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,20 +223,28 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||
if (typeInfo != null) {
|
||||
info = typeInfo.combine(info);
|
||||
}
|
||||
for (Map.Entry<String, Predicate<Class<?>>> entry : this.pathPrefixes.entrySet()) {
|
||||
if (entry.getValue().test(handlerType)) {
|
||||
String prefix = entry.getKey();
|
||||
if (this.embeddedValueResolver != null) {
|
||||
prefix = this.embeddedValueResolver.resolveStringValue(prefix);
|
||||
}
|
||||
info = RequestMappingInfo.paths(prefix).build().combine(info);
|
||||
break;
|
||||
}
|
||||
String prefix = getPathPrefix(handlerType);
|
||||
if (prefix != null) {
|
||||
info = RequestMappingInfo.paths(prefix).build().combine(info);
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
String getPathPrefix(Class<?> handlerType) {
|
||||
for (Map.Entry<String, Predicate<Class<?>>> entry : this.pathPrefixes.entrySet()) {
|
||||
if (entry.getValue().test(handlerType)) {
|
||||
String prefix = entry.getKey();
|
||||
if (this.embeddedValueResolver != null) {
|
||||
prefix = this.embeddedValueResolver.resolveStringValue(prefix);
|
||||
}
|
||||
return prefix;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to {@link #createRequestMappingInfo(RequestMapping, RequestCondition)},
|
||||
* supplying the appropriate custom {@link RequestCondition} depending on whether
|
||||
|
||||
@@ -56,6 +56,7 @@ import org.springframework.web.filter.ForwardedHeaderFilter;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
@@ -378,12 +379,9 @@ public class MvcUriComponentsBuilderTests {
|
||||
|
||||
@Test
|
||||
public void fromMappingNamePlain() {
|
||||
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
||||
context.setServletContext(new MockServletContext());
|
||||
context.register(WebConfig.class);
|
||||
context.refresh();
|
||||
|
||||
this.request.setAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
|
||||
initWebApplicationContext(WebConfig.class);
|
||||
|
||||
this.request.setServerName("example.org");
|
||||
this.request.setServerPort(9999);
|
||||
this.request.setContextPath("/base");
|
||||
@@ -395,12 +393,8 @@ public class MvcUriComponentsBuilderTests {
|
||||
|
||||
@Test
|
||||
public void fromMappingNameWithCustomBaseUrl() {
|
||||
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
||||
context.setServletContext(new MockServletContext());
|
||||
context.register(WebConfig.class);
|
||||
context.refresh();
|
||||
|
||||
this.request.setAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
|
||||
initWebApplicationContext(WebConfig.class);
|
||||
|
||||
UriComponentsBuilder baseUrl = UriComponentsBuilder.fromUriString("http://example.org:9999/base");
|
||||
MvcUriComponentsBuilder mvcBuilder = relativeTo(baseUrl);
|
||||
@@ -408,6 +402,41 @@ public class MvcUriComponentsBuilderTests {
|
||||
assertEquals("http://example.org:9999/base/people/123/addresses/DE", url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromControllerWithPrefix() {
|
||||
|
||||
initWebApplicationContext(PathPrefixWebConfig.class);
|
||||
|
||||
this.request.setServerName("example.org");
|
||||
this.request.setServerPort(9999);
|
||||
this.request.setContextPath("/base");
|
||||
|
||||
assertEquals("http://example.org:9999/base/api/people/123/addresses",
|
||||
fromController(PersonsAddressesController.class).buildAndExpand("123").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromMethodWithPrefix() {
|
||||
|
||||
initWebApplicationContext(PathPrefixWebConfig.class);
|
||||
|
||||
this.request.setServerName("example.org");
|
||||
this.request.setServerPort(9999);
|
||||
this.request.setContextPath("/base");
|
||||
|
||||
assertEquals("http://example.org:9999/base/api/people/123/addresses/DE",
|
||||
fromMethodCall(on(PersonsAddressesController.class).getAddressesForCountry("DE"))
|
||||
.buildAndExpand("123").toString());
|
||||
}
|
||||
|
||||
private void initWebApplicationContext(Class<?> configClass) {
|
||||
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
||||
context.setServletContext(new MockServletContext());
|
||||
context.register(configClass);
|
||||
context.refresh();
|
||||
this.request.setAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
|
||||
}
|
||||
|
||||
|
||||
static class Person {
|
||||
|
||||
@@ -555,6 +584,21 @@ public class MvcUriComponentsBuilderTests {
|
||||
}
|
||||
|
||||
|
||||
@EnableWebMvc
|
||||
static class PathPrefixWebConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void configurePathMatch(PathMatchConfigurer configurer) {
|
||||
configurer.addPathPrefix("/api", PersonsAddressesController.class::equals);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PersonsAddressesController controller() {
|
||||
return new PersonsAddressesController();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/hotels/{hotel}")
|
||||
static class BookingControllerWithModelAndView {
|
||||
|
||||
Reference in New Issue
Block a user