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 1a28e7638..2a8046578 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 @@ -22,6 +22,7 @@ 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; @@ -122,9 +123,11 @@ public class BasePathAwareHandlerMapping extends RequestMappingHandlerMapping { } ProducesRequestCondition producesCondition = customize(info.getProducesCondition()); + Set mediaTypes = producesCondition.getProducibleMediaTypes(); - return new RequestMappingInfo(info.getPatternsCondition(), info.getMethodsCondition(), info.getParamsCondition(), - info.getHeadersCondition(), info.getConsumesCondition(), producesCondition, info.getCustomCondition()); + return info.mutate() + .produces(mediaTypes.stream().map(MediaType::toString).toArray(String[]::new)) + .build(); } /** diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMapping.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMapping.java index ef3a88e17..616818ea8 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMapping.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMapping.java @@ -44,6 +44,7 @@ import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition; 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; @@ -266,13 +267,29 @@ public class RepositoryRestHandlerMapping extends BasePathAwareHandlerMapping { return; } - String pattern = mappingInfo.getPatternsCondition() // - .getMatchingCondition(request)// - .getPatterns() // - .iterator().next(); + String pattern = getPattern(mappingInfo, request); + + PathPatternParser parser = getPatternParser(); + parser = parser != null ? parser : PARSER; request.setAttribute(EFFECTIVE_LOOKUP_PATH_ATTRIBUTE, - PARSER.parse(pattern.replace("/{repository}", repositoryBasePath))); + parser.parse(pattern.replace("/{repository}", repositoryBasePath))); + } + + private static String getPattern(RequestMappingInfo info, HttpServletRequest request) { + + PathPatternsRequestCondition pathPatternsCondition = info.getPathPatternsCondition(); + + if (pathPatternsCondition != null) { + return pathPatternsCondition + .getMatchingCondition(request) + .getFirstPattern().getPatternString(); + } + + return info.getPatternsCondition() // + .getMatchingCondition(request)// + .getPatterns() + .iterator().next(); } /** diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/DelegatingHandlerMapping.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/DelegatingHandlerMapping.java index ae2813505..d5f7fcbdf 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/DelegatingHandlerMapping.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/DelegatingHandlerMapping.java @@ -22,6 +22,7 @@ import java.util.Objects; import javax.servlet.http.HttpServletRequest; import org.springframework.core.Ordered; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.HttpMediaTypeNotSupportedException; @@ -29,7 +30,6 @@ import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.UnsatisfiedServletRequestParameterException; import org.springframework.web.servlet.HandlerExecutionChain; import org.springframework.web.servlet.HandlerMapping; -import org.springframework.web.servlet.handler.AbstractHandlerMapping; import org.springframework.web.servlet.handler.MatchableHandlerMapping; import org.springframework.web.servlet.handler.RequestMatchResult; import org.springframework.web.util.pattern.PathPatternParser; @@ -45,25 +45,28 @@ class DelegatingHandlerMapping implements org.springframework.data.rest.webmvc.support.DelegatingHandlerMapping, Ordered { private final List delegates; + private final @Nullable PathPatternParser parser; /** * Creates a new {@link DelegatingHandlerMapping} for the given delegates. * * @param delegates must not be {@literal null}. */ - public DelegatingHandlerMapping(List delegates) { + public DelegatingHandlerMapping(List delegates, @Nullable PathPatternParser parser) { Assert.notNull(delegates, "Delegates must not be null!"); this.delegates = delegates; + this.parser = parser; } - void setPatternParser(PathPatternParser parser) { - - delegates.stream() // - .filter(AbstractHandlerMapping.class::isInstance) // - .map(AbstractHandlerMapping.class::cast) // - .forEach(it -> it.setPatternParser(parser)); + /* + * (non-Javadoc) + * @see org.springframework.web.servlet.HandlerMapping#usesPathPatterns() + */ + @Override + public boolean usesPathPatterns() { + return parser != null; } @SuppressWarnings("all") diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java index 65ebe45fd..016add316 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java @@ -119,11 +119,11 @@ import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.HandlerMapping; -import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.handler.AbstractHandlerMapping; import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; +import org.springframework.web.util.pattern.PathPatternParser; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.Module; @@ -170,9 +170,11 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon ObjectProvider invoker; ObjectProvider resolver; ObjectProvider geoModule; + ConversionService defaultConversionService; private final Lazy mapper; + private final ObjectProvider parser; private ClassLoader beanClassLoader; @@ -183,7 +185,6 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon private Lazy baseUri; private Lazy resourceMappings; private Lazy repositories; - private Lazy restHandlerMapping; private Lazy resourceMetadataHandlerMethodArgumentResolver; private Lazy excerptProjector; private Lazy persistentEntities; @@ -206,7 +207,8 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon ObjectProvider objectMapper, // ObjectProvider invoker, // ObjectProvider resolver, // - ObjectProvider geoModule) { + ObjectProvider geoModule, // + ObjectProvider parser) { super(context, conversionService); @@ -217,6 +219,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon this.invoker = invoker; this.resolver = resolver; this.geoModule = geoModule; + this.parser = parser; this.mapper = Lazy.of(() -> { @@ -241,7 +244,6 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon this.baseUri = Lazy.of(() -> context.getBean(BaseUri.class)); this.resourceMappings = Lazy.of(() -> context.getBean(RepositoryResourceMappings.class)); this.repositories = Lazy.of(() -> context.getBean(Repositories.class)); - this.restHandlerMapping = Lazy.of(() -> context.getBean("restHandlerMapping", DelegatingHandlerMapping.class)); this.resourceMetadataHandlerMethodArgumentResolver = Lazy .of(() -> context.getBean(ResourceMetadataHandlerMethodArgumentResolver.class)); this.excerptProjector = Lazy.of(() -> context.getBean(ExcerptProjector.class)); @@ -347,6 +349,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon * Main configuration for the REST exporter. */ @Bean + @SuppressWarnings("unchecked") public T repositoryRestConfiguration() { ProjectionDefinitionConfiguration configuration = new ProjectionDefinitionConfiguration(); @@ -643,33 +646,27 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon RepositoryRestConfiguration repositoryRestConfiguration, CorsConfigurationAware corsRestConfiguration) { Map corsConfigurations = corsRestConfiguration.getCorsConfigurations(); + PathPatternParser parser = this.parser.getIfAvailable(); RepositoryRestHandlerMapping repositoryMapping = new RepositoryRestHandlerMapping(resourceMappings, repositoryRestConfiguration, repositories); repositoryMapping.setJpaHelper(jpaHelper.orElse(null)); repositoryMapping.setApplicationContext(applicationContext); repositoryMapping.setCorsConfigurations(corsConfigurations); + repositoryMapping.setPatternParser(parser); repositoryMapping.afterPropertiesSet(); BasePathAwareHandlerMapping basePathMapping = new BasePathAwareHandlerMapping(repositoryRestConfiguration); basePathMapping.setApplicationContext(applicationContext); basePathMapping.setCorsConfigurations(corsConfigurations); + basePathMapping.setPatternParser(parser); basePathMapping.afterPropertiesSet(); List mappings = new ArrayList<>(); mappings.add(basePathMapping); mappings.add(repositoryMapping); - return new DelegatingHandlerMapping(mappings); - } - - /* - * (non-Javadoc) - * @see org.springframework.web.servlet.config.annotation.WebMvcConfigurer#configurePathMatch(org.springframework.web.servlet.config.annotation.PathMatchConfigurer) - */ - @Override - public void configurePathMatch(PathMatchConfigurer configurer) { - restHandlerMapping.get().setPatternParser(configurer.getPatternParser()); + return new DelegatingHandlerMapping(mappings, parser); } @Bean diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/DelegatingHandlerMappingUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/DelegatingHandlerMappingUnitTests.java index a7afbf481..9d55a9ff5 100755 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/DelegatingHandlerMappingUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/DelegatingHandlerMappingUnitTests.java @@ -52,7 +52,7 @@ public class DelegatingHandlerMappingUnitTests { @Test // DATAREST-490, DATAREST-522, DATAREST-1387 public void consultsAllHandlerMappingsAndThrowsExceptionEventually() throws Exception { - DelegatingHandlerMapping mapping = new DelegatingHandlerMapping(Arrays.asList(first, second)); + DelegatingHandlerMapping mapping = new DelegatingHandlerMapping(Arrays.asList(first, second), null); assertHandlerTriedButExceptionThrown(mapping, HttpMediaTypeNotAcceptableException.class); assertHandlerTriedButExceptionThrown(mapping, HttpRequestMethodNotSupportedException.class); @@ -73,7 +73,7 @@ public class DelegatingHandlerMappingUnitTests { MatchableHandlerMapping fourth = mock(MatchableHandlerMapping.class, Answers.RETURNS_MOCKS); doReturn(result).when(fourth).match(any(), any(String.class)); - DelegatingHandlerMapping mapping = new DelegatingHandlerMapping(Arrays.asList(first, second, third, fourth)); + DelegatingHandlerMapping mapping = new DelegatingHandlerMapping(Arrays.asList(first, second, third, fourth), null); assertThat(mapping.match(request, "somePattern")).isEqualTo(result); }