From d7f36b118060157e208aa044eb5c1c038b2c1dc2 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Wed, 1 Jul 2020 18:26:28 +0200 Subject: [PATCH] DATAREST-1540 - Improvements in HandlerMapping implementation for Spring 5.3. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now use Spring's path prefix capabilities to apply Spring Data REST's base path to its mappings. This was previously implemented by tweaking the matching conditions. We now also pick up Spring 5.3's PathPatternParser and apply that to the custom HandlerMapping implementations we register. Moved DelegatingHandlerMapping into the configuration package to be able to keep it around in package scope. Deprecated RepositoryRestConfiguration.getBaseUri() as we have only supported paths for quite a while now. Moved all client code to ….getBasePath() instead. --- .../config/RepositoryRestConfiguration.java | 4 +- ...oryRestHandlerMappingIntegrationTests.java | 4 +- .../webmvc/BasePathAwareHandlerMapping.java | 466 +----------------- .../data/rest/webmvc/ProfileController.java | 2 +- .../webmvc/RepositoryRestHandlerMapping.java | 7 +- .../DelegatingHandlerMapping.java | 28 +- .../RepositoryRestMvcConfiguration.java | 25 +- .../webmvc/support/RepositoryEntityLinks.java | 2 +- .../BasePathAwareHandlerMappingUnitTests.java | 15 +- ...RepositoryRestHandlerMappingUnitTests.java | 56 +-- .../DelegatingHandlerMappingUnitTests.java | 2 +- 11 files changed, 111 insertions(+), 500 deletions(-) rename spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/{support => config}/DelegatingHandlerMapping.java (86%) rename spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/{support => config}/DelegatingHandlerMappingUnitTests.java (98%) diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/config/RepositoryRestConfiguration.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/config/RepositoryRestConfiguration.java index bc84a664c..5ce09c895 100644 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/config/RepositoryRestConfiguration.java +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/config/RepositoryRestConfiguration.java @@ -51,7 +51,7 @@ @SuppressWarnings("deprecation") public class RepositoryRestConfiguration { - private static final URI NO_URI = URI.create(""); + static final URI NO_URI = URI.create(""); private URI baseUri = NO_URI; private URI basePath = NO_URI; @@ -109,7 +109,9 @@ public RepositoryRestConfiguration(ProjectionDefinitionConfiguration projectionC * The base URI against which the exporter should calculate its links. * * @return The base URI. + * @deprecated use {@link #getBasePath()} instead. */ + @Deprecated public URI getBaseUri() { return basePath != NO_URI ? basePath : baseUri; } diff --git a/spring-data-rest-tests/spring-data-rest-tests-mongodb/src/test/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMappingIntegrationTests.java b/spring-data-rest-tests/spring-data-rest-tests-mongodb/src/test/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMappingIntegrationTests.java index 2f4ec4bcd..c716d4a30 100755 --- a/spring-data-rest-tests/spring-data-rest-tests-mongodb/src/test/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMappingIntegrationTests.java +++ b/spring-data-rest-tests/spring-data-rest-tests-mongodb/src/test/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMappingIntegrationTests.java @@ -21,11 +21,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.rest.tests.AbstractControllerIntegrationTests; import org.springframework.data.rest.tests.mongodb.MongoDbRepositoryConfig; -import org.springframework.data.rest.webmvc.support.DelegatingHandlerMapping; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.test.context.ContextConfiguration; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerExecutionChain; +import org.springframework.web.servlet.HandlerMapping; /** * Integration tests for {@link BasePathAwareHandlerMapping}. @@ -36,7 +36,7 @@ @ContextConfiguration(classes = MongoDbRepositoryConfig.class) public class RepositoryRestHandlerMappingIntegrationTests extends AbstractControllerIntegrationTests { - @Autowired DelegatingHandlerMapping mapping; + @Autowired HandlerMapping mapping; @Test // DATAREST-617 public void usesMethodsWithoutProducesClauseForGeneralJsonRequests() throws Exception { 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 15a795b7f..6061e2cd3 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 @@ -15,35 +15,17 @@ */ package org.springframework.data.rest.webmvc; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.lang.reflect.Method; -import java.net.URI; -import java.security.Principal; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.Enumeration; +import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; -import java.util.Set; - -import javax.servlet.AsyncContext; -import javax.servlet.DispatcherType; -import javax.servlet.RequestDispatcher; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletInputStream; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.Cookie; +import java.util.function.Predicate; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import javax.servlet.http.Part; import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.util.ProxyUtils; @@ -52,11 +34,9 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.web.method.HandlerMethod; -import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition; 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 org.springframework.web.util.UrlPathHelper; /** * A {@link RequestMappingHandlerMapping} that augments the request mappings @@ -65,12 +45,8 @@ */ public class BasePathAwareHandlerMapping extends RequestMappingHandlerMapping { - private static final UrlPathHelper URL_PATH_HELPER = new UrlPathHelper(); - private final RepositoryRestConfiguration configuration; - private String prefix; - /** * Creates a new {@link BasePathAwareHandlerMapping} using the given {@link RepositoryRestConfiguration}. * @@ -79,7 +55,18 @@ public class BasePathAwareHandlerMapping extends RequestMappingHandlerMapping { public BasePathAwareHandlerMapping(RepositoryRestConfiguration configuration) { 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); + } } /* @@ -134,34 +121,12 @@ protected RequestMappingInfo getMappingForMethod(Method method, Class handler return null; } - PatternsRequestCondition patternsCondition = customize(info.getPatternsCondition(), prefix); ProducesRequestCondition producesCondition = customize(info.getProducesCondition()); - return new RequestMappingInfo(patternsCondition, info.getMethodsCondition(), info.getParamsCondition(), + return new RequestMappingInfo(info.getPatternsCondition(), info.getMethodsCondition(), info.getParamsCondition(), info.getHeadersCondition(), info.getConsumesCondition(), producesCondition, info.getCustomCondition()); } - /** - * Customize the given {@link PatternsRequestCondition} and prefix. - * - * @param condition will never be {@literal null}. - * @param prefix will never be {@literal null}. - * @return - */ - protected PatternsRequestCondition customize(PatternsRequestCondition condition, String prefix) { - - Set patterns = condition.getPatterns(); - String[] augmentedPatterns = new String[patterns.size()]; - int count = 0; - - for (String pattern : patterns) { - augmentedPatterns[count++] = prefix.concat(pattern); - } - - return new PatternsRequestCondition(augmentedPatterns, getUrlPathHelper(), getPathMatcher(), - useSuffixPatternMatch(), useTrailingSlashMatch(), getFileExtensions()); - } - /** * Customize the given {@link ProducesRequestCondition}. Default implementation returns the condition as is. * @@ -184,391 +149,6 @@ protected boolean isHandler(Class beanType) { return type.isAnnotationPresent(BasePathAwareController.class); } - /* - * (non-Javadoc) - * @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet() - */ - @Override - public void afterPropertiesSet() { - - URI baseUri = configuration.getBaseUri(); - - if (baseUri.isAbsolute()) { - HttpServletRequest request = new UriAwareHttpServletRequest(getServletContext(), baseUri); - this.prefix = URL_PATH_HELPER.getPathWithinApplication(request); - } else { - this.prefix = baseUri.toString(); - } - - super.afterPropertiesSet(); - } - - private static class UriAwareHttpServletRequest implements HttpServletRequest { - - private final ServletContext context; - private final String path; - - /** - * @param context - * @param uri - */ - public UriAwareHttpServletRequest(ServletContext context, URI uri) { - this.context = context; - this.path = uri.getPath(); - } - - /* - * (non-Javadoc) - * @see javax.servlet.ServletRequest#getAttribute(java.lang.String) - */ - @Override - public Object getAttribute(String name) { - return null; - } - - @Override - public Enumeration getAttributeNames() { - throw new UnsupportedOperationException(); - } - - /* - * (non-Javadoc) - * @see javax.servlet.ServletRequest#getCharacterEncoding() - */ - @Override - public String getCharacterEncoding() { - return null; - } - - @Override - public void setCharacterEncoding(String env) throws UnsupportedEncodingException { - throw new UnsupportedOperationException(); - } - - @Override - public int getContentLength() { - throw new UnsupportedOperationException(); - } - - @Override - public String getContentType() { - throw new UnsupportedOperationException(); - } - - @Override - public ServletInputStream getInputStream() throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - public String getParameter(String name) { - throw new UnsupportedOperationException(); - } - - @Override - public Enumeration getParameterNames() { - throw new UnsupportedOperationException(); - } - - @Override - public String[] getParameterValues(String name) { - throw new UnsupportedOperationException(); - } - - @Override - public Map getParameterMap() { - throw new UnsupportedOperationException(); - } - - @Override - public String getProtocol() { - throw new UnsupportedOperationException(); - } - - @Override - public String getScheme() { - throw new UnsupportedOperationException(); - } - - @Override - public String getServerName() { - throw new UnsupportedOperationException(); - } - - @Override - public int getServerPort() { - throw new UnsupportedOperationException(); - } - - @Override - public BufferedReader getReader() throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - public String getRemoteAddr() { - throw new UnsupportedOperationException(); - } - - @Override - public String getRemoteHost() { - throw new UnsupportedOperationException(); - } - - @Override - public void setAttribute(String name, Object o) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeAttribute(String name) { - throw new UnsupportedOperationException(); - } - - @Override - public Locale getLocale() { - throw new UnsupportedOperationException(); - } - - @Override - public Enumeration getLocales() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isSecure() { - throw new UnsupportedOperationException(); - } - - @Override - public RequestDispatcher getRequestDispatcher(String path) { - throw new UnsupportedOperationException(); - } - - @Override - public String getRealPath(String path) { - throw new UnsupportedOperationException(); - } - - @Override - public int getRemotePort() { - throw new UnsupportedOperationException(); - } - - @Override - public String getLocalName() { - throw new UnsupportedOperationException(); - } - - @Override - public String getLocalAddr() { - throw new UnsupportedOperationException(); - } - - @Override - public int getLocalPort() { - throw new UnsupportedOperationException(); - } - - /* - * (non-Javadoc) - * @see javax.servlet.ServletRequest#getServletContext() - */ - @Override - public ServletContext getServletContext() { - return context; - } - - @Override - public AsyncContext startAsync() throws IllegalStateException { - throw new UnsupportedOperationException(); - } - - @Override - public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) - throws IllegalStateException { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isAsyncStarted() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isAsyncSupported() { - throw new UnsupportedOperationException(); - } - - @Override - public AsyncContext getAsyncContext() { - throw new UnsupportedOperationException(); - } - - @Override - public DispatcherType getDispatcherType() { - throw new UnsupportedOperationException(); - } - - @Override - public String getAuthType() { - throw new UnsupportedOperationException(); - } - - @Override - public Cookie[] getCookies() { - throw new UnsupportedOperationException(); - } - - @Override - public long getDateHeader(String name) { - throw new UnsupportedOperationException(); - } - - @Override - public String getHeader(String name) { - throw new UnsupportedOperationException(); - } - - @Override - public Enumeration getHeaders(String name) { - throw new UnsupportedOperationException(); - } - - @Override - public Enumeration getHeaderNames() { - throw new UnsupportedOperationException(); - } - - @Override - public int getIntHeader(String name) { - throw new UnsupportedOperationException(); - } - - @Override - public String getMethod() { - throw new UnsupportedOperationException(); - } - - @Override - public String getPathInfo() { - throw new UnsupportedOperationException(); - } - - @Override - public String getPathTranslated() { - throw new UnsupportedOperationException(); - } - - /* - * (non-Javadoc) - * @see javax.servlet.http.HttpServletRequest#getContextPath() - */ - @Override - public String getContextPath() { - return context.getContextPath(); - } - - @Override - public String getQueryString() { - throw new UnsupportedOperationException(); - } - - @Override - public String getRemoteUser() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isUserInRole(String role) { - throw new UnsupportedOperationException(); - } - - @Override - public Principal getUserPrincipal() { - throw new UnsupportedOperationException(); - } - - @Override - public String getRequestedSessionId() { - throw new UnsupportedOperationException(); - } - - /* - * (non-Javadoc) - * @see javax.servlet.http.HttpServletRequest#getRequestURI() - */ - @Override - public String getRequestURI() { - return path; - } - - @Override - public StringBuffer getRequestURL() { - throw new UnsupportedOperationException(); - } - - @Override - public String getServletPath() { - throw new UnsupportedOperationException(); - } - - @Override - public HttpSession getSession(boolean create) { - throw new UnsupportedOperationException(); - } - - @Override - public HttpSession getSession() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isRequestedSessionIdValid() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isRequestedSessionIdFromCookie() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isRequestedSessionIdFromURL() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isRequestedSessionIdFromUrl() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { - throw new UnsupportedOperationException(); - } - - @Override - public void login(String username, String password) throws ServletException { - throw new UnsupportedOperationException(); - } - - @Override - public void logout() throws ServletException { - throw new UnsupportedOperationException(); - } - - @Override - public Collection getParts() throws IOException, ServletException { - throw new UnsupportedOperationException(); - } - - @Override - public Part getPart(String name) throws IOException, ServletException { - throw new UnsupportedOperationException(); - } - } - /** * {@link HttpServletRequest} that exposes the given media types for the {@code Accept} header. * @@ -610,11 +190,9 @@ public CustomAcceptHeaderHttpServletRequest(HttpServletRequest request, List getHeaders(String name) { - if (HttpHeaders.ACCEPT.equalsIgnoreCase(name) && acceptMediaTypes != null) { - return Collections.enumeration(acceptMediaTypeStrings); - } - - return super.getHeaders(name); + return HttpHeaders.ACCEPT.equalsIgnoreCase(name) && acceptMediaTypes != null // + ? Collections.enumeration(acceptMediaTypeStrings) // + : super.getHeaders(name); } } } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/ProfileController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/ProfileController.java index 31015b4ad..70e8adf92 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/ProfileController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/ProfileController.java @@ -118,7 +118,7 @@ HttpEntity> listAllFormsOfMetadata() { */ public static String getRootPath(RepositoryRestConfiguration configuration) { - BaseUri baseUri = new BaseUri(configuration.getBaseUri()); + BaseUri baseUri = new BaseUri(configuration.getBasePath()); return baseUri.getUriComponentsBuilder().path(ProfileController.PROFILE_ROOT_MAPPING).build().toString(); } 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 c54d36cd0..5d2a7aba6 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 @@ -19,7 +19,6 @@ import lombok.RequiredArgsConstructor; import java.util.Collections; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; @@ -147,7 +146,7 @@ protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletReques return null; } - String repositoryLookupPath = new BaseUri(configuration.getBaseUri()).getRepositoryLookupPath(lookupPath); + String repositoryLookupPath = new BaseUri(configuration.getBasePath()).getRepositoryLookupPath(lookupPath); // Repository root resource if (!StringUtils.hasText(repositoryLookupPath)) { @@ -210,7 +209,7 @@ protected ProducesRequestCondition customize(ProducesRequestCondition condition) return condition; } - HashSet mediaTypes = new LinkedHashSet(); + Set mediaTypes = new LinkedHashSet(); mediaTypes.add(configuration.getDefaultMediaType().toString()); mediaTypes.add(MediaType.APPLICATION_JSON_VALUE); @@ -224,7 +223,7 @@ protected ProducesRequestCondition customize(ProducesRequestCondition condition) protected CorsConfiguration getCorsConfiguration(Object handler, HttpServletRequest request) { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); - String repositoryLookupPath = new BaseUri(configuration.getBaseUri()).getRepositoryLookupPath(lookupPath); + String repositoryLookupPath = new BaseUri(configuration.getBasePath()).getRepositoryLookupPath(lookupPath); CorsConfiguration corsConfiguration = super.getCorsConfiguration(handler, request); return repositories.filter(it -> StringUtils.hasText(repositoryLookupPath))// diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/DelegatingHandlerMapping.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/DelegatingHandlerMapping.java similarity index 86% rename from spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/DelegatingHandlerMapping.java rename to spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/DelegatingHandlerMapping.java index 2156297d1..7ed37d650 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/DelegatingHandlerMapping.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/DelegatingHandlerMapping.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.rest.webmvc.support; +package org.springframework.data.rest.webmvc.config; import lombok.Getter; import lombok.NonNull; @@ -31,6 +31,7 @@ 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; @@ -42,7 +43,7 @@ * @author Oliver Gierke * @soundtrack Benny Greb - Stabila (Moving Parts) */ -public class DelegatingHandlerMapping implements HandlerMapping, Ordered, MatchableHandlerMapping { +class DelegatingHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping { private final @Getter List delegates; @@ -58,6 +59,17 @@ public DelegatingHandlerMapping(List delegates) { this.delegates = delegates; } + @Override + public void setPatternParser(PathPatternParser parser) { + + super.setPatternParser(parser); + + delegates.stream() // + .filter(AbstractHandlerMapping.class::isInstance) // + .map(AbstractHandlerMapping.class::cast) // + .forEach(it -> it.setPatternParser(parser)); + } + /* * (non-Javadoc) * @see org.springframework.core.Ordered#getOrder() @@ -69,10 +81,10 @@ public int getOrder() { /* * (non-Javadoc) - * @see org.springframework.web.servlet.HandlerMapping#getHandler(javax.servlet.http.HttpServletRequest) + * @see org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandlerInternal(javax.servlet.http.HttpServletRequest) */ @Override - public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { + protected Object getHandlerInternal(HttpServletRequest request) throws Exception { return HandlerSelectionResult.from(request, delegates).resultOrException(); } @@ -90,14 +102,6 @@ public RequestMatchResult match(HttpServletRequest request, String pattern) { } } - /* - * (non-Javadoc) - * @see org.springframework.web.servlet.handler.MatchableHandlerMapping#getPatternParser() - */ - public PathPatternParser getPatternParser() { - return null; - } - @Value private static class HandlerSelectionResult { 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 97842b16f..e9a694b0f 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 @@ -80,7 +80,15 @@ import org.springframework.data.rest.webmvc.mapping.LinkCollector; import org.springframework.data.rest.webmvc.spi.BackendIdConverter; import org.springframework.data.rest.webmvc.spi.BackendIdConverter.DefaultIdConverter; -import org.springframework.data.rest.webmvc.support.*; +import org.springframework.data.rest.webmvc.support.BackendIdHandlerMethodArgumentResolver; +import org.springframework.data.rest.webmvc.support.DefaultExcerptProjector; +import org.springframework.data.rest.webmvc.support.DomainClassResolver; +import org.springframework.data.rest.webmvc.support.ETagArgumentResolver; +import org.springframework.data.rest.webmvc.support.ExcerptProjector; +import org.springframework.data.rest.webmvc.support.HttpMethodHandlerMethodArgumentResolver; +import org.springframework.data.rest.webmvc.support.JpaHelper; +import org.springframework.data.rest.webmvc.support.PagingAndSortingTemplateVariables; +import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks; import org.springframework.data.util.AnnotatedTypeScanner; import org.springframework.data.util.Lazy; import org.springframework.data.web.HateoasPageableHandlerMethodArgumentResolver; @@ -114,7 +122,9 @@ 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.servlet.mvc.method.annotation.ResponseBodyAdvice; @@ -297,7 +307,7 @@ public MetadataConfiguration metadataConfiguration() { @Bean public BaseUri baseUri() { - return new BaseUri(repositoryRestConfiguration().getBaseUri()); + return new BaseUri(repositoryRestConfiguration().getBasePath()); } /** @@ -530,7 +540,7 @@ public RequestMappingHandlerAdapter repositoryExporterHandlerAdapter() { * @return */ @Bean - public DelegatingHandlerMapping restHandlerMapping() { + public AbstractHandlerMapping restHandlerMapping() { Map corsConfigurations = repositoryRestConfiguration().getCorsRegistry() .getCorsConfigurations(); @@ -554,6 +564,15 @@ public DelegatingHandlerMapping restHandlerMapping() { 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().setPatternParser(configurer.getPatternParser()); + } + @Bean public RepositoryResourceMappings resourceMappings() { return new RepositoryResourceMappings(repositories(), persistentEntities(), repositoryRestConfiguration()); diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryEntityLinks.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryEntityLinks.java index fa2eae377..d43baf65b 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryEntityLinks.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryEntityLinks.java @@ -86,7 +86,7 @@ public boolean supports(Class delimiter) { public LinkBuilder linkFor(Class type) { ResourceMetadata metadata = mappings.getMetadataFor(type); - return new RepositoryLinkBuilder(metadata, new BaseUri(config.getBaseUri())); + return new RepositoryLinkBuilder(metadata, new BaseUri(config.getBasePath())); } /* diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/BasePathAwareHandlerMappingUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/BasePathAwareHandlerMappingUnitTests.java index bbca84fda..9bb7cb59d 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/BasePathAwareHandlerMappingUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/BasePathAwareHandlerMappingUnitTests.java @@ -18,6 +18,9 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; +import java.net.URI; + +import org.junit.Before; import org.junit.Test; import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.support.AopUtils; @@ -30,8 +33,16 @@ */ public class BasePathAwareHandlerMappingUnitTests { - RepositoryRestConfiguration configuration = mock(RepositoryRestConfiguration.class); - HandlerMappingStub mapping = new HandlerMappingStub(configuration); + HandlerMappingStub mapping; + + @Before + public void setUp() { + + RepositoryRestConfiguration configuration = mock(RepositoryRestConfiguration.class); + doReturn(URI.create("")).when(configuration).getBasePath(); + + mapping = new HandlerMappingStub(configuration); + } @Test // DATAREST-1132 public void detectsAnnotationsOnProxies() { diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMappingUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMappingUnitTests.java index 207fc3066..afee80ba9 100755 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMappingUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryRestHandlerMappingUnitTests.java @@ -19,7 +19,9 @@ import static org.mockito.Mockito.*; import java.lang.reflect.Method; +import java.net.URI; import java.util.Collections; +import java.util.function.Supplier; import javax.servlet.http.HttpServletRequest; @@ -71,7 +73,7 @@ public class RepositoryRestHandlerMappingUnitTests { @Mock Repositories repositories; RepositoryRestConfiguration configuration; - HandlerMappingStub handlerMapping; + Supplier handlerMapping; MockHttpServletRequest mockRequest; Method listEntitiesMethod, rootHandlerMethod; @@ -81,8 +83,14 @@ public void setUp() throws Exception { configuration = new RepositoryRestConfiguration(new ProjectionDefinitionConfiguration(), new MetadataConfiguration(), mock(EnumTranslationConfiguration.class)); - handlerMapping = new HandlerMappingStub(mappings, configuration, repositories); - handlerMapping.setApplicationContext(CONTEXT); + handlerMapping = () -> { + + HandlerMappingStub mapping = new HandlerMappingStub(mappings, configuration, repositories); + mapping.setApplicationContext(CONTEXT); + mapping.afterPropertiesSet(); + + return mapping; + }; mockRequest = new MockHttpServletRequest(); @@ -103,9 +111,7 @@ public void rejectsNullConfiguration() { @Test // DATAREST-111 public void returnsNullForUriNotMapped() throws Exception { - - handlerMapping.afterPropertiesSet(); - assertThat(handlerMapping.getHandler(mockRequest)).isNull(); + assertThat(handlerMapping.get().getHandler(mockRequest)).isNull(); } @Test // DATAREST-111 @@ -114,8 +120,7 @@ public void looksUpRepositoryEntityControllerMethodCorrectly() throws Exception when(mappings.exportsTopLevelResourceFor("/people")).thenReturn(true); mockRequest = new MockHttpServletRequest("GET", "/people"); - handlerMapping.afterPropertiesSet(); - HandlerMethod method = handlerMapping.getHandlerInternal(mockRequest); + HandlerMethod method = handlerMapping.get().getHandlerInternal(mockRequest); assertThat(method).isNotNull(); assertThat(method.getMethod()).isEqualTo(listEntitiesMethod); @@ -128,9 +133,8 @@ public void returnsRepositoryHandlerMethodWithBaseUriConfigured() throws Excepti mockRequest = new MockHttpServletRequest("GET", "/base/people"); configuration.setBasePath("/base"); - handlerMapping.afterPropertiesSet(); - HandlerMethod method = handlerMapping.getHandlerInternal(mockRequest); + HandlerMethod method = handlerMapping.get().getHandlerInternal(mockRequest); assertThat(method).isNotNull(); assertThat(method.getMethod()).isEqualTo(listEntitiesMethod); @@ -142,9 +146,8 @@ public void returnsRootHandlerMethodWithBaseUriConfigured() throws Exception { mockRequest = new MockHttpServletRequest("GET", "/base"); configuration.setBasePath("/base"); - handlerMapping.afterPropertiesSet(); - HandlerMethod method = handlerMapping.getHandlerInternal(mockRequest); + HandlerMethod method = handlerMapping.get().getHandlerInternal(mockRequest); assertThat(method).isNotNull(); assertThat(method.getMethod()).isEqualTo(rootHandlerMethod); @@ -157,9 +160,8 @@ public void returnsRepositoryHandlerMethodForAbsoluteBaseUri() throws Exception mockRequest = new MockHttpServletRequest("GET", "/base/people/"); configuration.setBasePath("/base"); - handlerMapping.afterPropertiesSet(); - HandlerMethod method = handlerMapping.getHandlerInternal(mockRequest); + HandlerMethod method = handlerMapping.get().getHandlerInternal(mockRequest); assertThat(method).isNotNull(); assertThat(method.getMethod()).isEqualTo(listEntitiesMethod); @@ -173,9 +175,8 @@ public void returnsRepositoryHandlerMethodForAbsoluteBaseUriWithServletMapping() mockRequest.setServletPath("/base/people"); configuration.setBasePath("/base"); - handlerMapping.afterPropertiesSet(); - HandlerMethod method = handlerMapping.getHandlerInternal(mockRequest); + HandlerMethod method = handlerMapping.get().getHandlerInternal(mockRequest); assertThat(method).isNotNull(); assertThat(method.getMethod()).isEqualTo(listEntitiesMethod); @@ -189,7 +190,7 @@ public void refrainsFromMappingIfTheRequestDoesNotPointIntoAbsolutelyDefinedUriS configuration.setBasePath("/base"); - HandlerMethod method = handlerMapping.getHandlerInternal(mockRequest); + HandlerMethod method = handlerMapping.get().getHandlerInternal(mockRequest); assertThat(method).isNull(); } @@ -206,7 +207,7 @@ public void refrainsFromMappingWhenUrisDontMatch() throws Exception { configuration.setBasePath(baseUri); - HandlerMethod method = handlerMapping.getHandlerInternal(mockRequest); + HandlerMethod method = handlerMapping.get().getHandlerInternal(mockRequest); assertThat(method).isNull(); } @@ -218,7 +219,7 @@ public void rejectsUnexpandedUriTemplateWithNotFound() throws Exception { mockRequest = new MockHttpServletRequest("GET", "/people{?projection}"); - assertThat(handlerMapping.getHandler(mockRequest)).isNull(); + assertThat(handlerMapping.get().getHandler(mockRequest)).isNull(); } @Test // DATAREST-1019 @@ -233,7 +234,7 @@ public void resolvesCorsConfigurationFromRequestUri() { mockRequest = new MockHttpServletRequest("GET", uri); mockRequest.setServletPath(uri); - handlerMapping.getCorsConfiguration(uri, mockRequest); + handlerMapping.get().getCorsConfiguration(uri, mockRequest); verify(mappings).exportsTopLevelResourceFor("/people"); } @@ -253,7 +254,7 @@ public void stripsBaseUriForCorsConfigurationResolution() { mockRequest = new MockHttpServletRequest("GET", uri); mockRequest.setServletPath(uri); - handlerMapping.getCorsConfiguration(uri, mockRequest); + handlerMapping.get().getCorsConfiguration(uri, mockRequest); verify(mappings).exportsTopLevelResourceFor("/people"); } @@ -268,8 +269,10 @@ public void detectsAnnotationsOnProxies() { Class type = createProxy(new SomeController()); - HandlerMappingStub mapping = new HandlerMappingStub(mock(ResourceMappings.class), - mock(RepositoryRestConfiguration.class)); + RepositoryRestConfiguration configuration = mock(RepositoryRestConfiguration.class); + doReturn(URI.create("")).when(configuration).getBasePath(); + + HandlerMappingStub mapping = new HandlerMappingStub(mock(ResourceMappings.class), configuration); assertThat(mapping.isHandler(type)).isTrue(); } @@ -281,8 +284,7 @@ public void exposesEffectiveRepositoryLookupPathAsRequestAttribute() throws Exce MockHttpServletRequest mockRequest = new MockHttpServletRequest("GET", "/people/search/findByLastnameLike"); - handlerMapping.afterPropertiesSet(); - handlerMapping.getHandlerInternal(mockRequest); + handlerMapping.get().getHandlerInternal(mockRequest); assertThat(mockRequest.getAttribute(RepositoryRestHandlerMapping.EFFECTIVE_LOOKUP_PATH_ATTRIBUTE)) // .isInstanceOfSatisfying(PathPattern.class, it -> { @@ -299,9 +301,7 @@ public void handlesCorsPreflightRequestsProperly() throws Exception { request.addHeader(HttpHeaders.ORIGIN, "test case"); request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); - handlerMapping.afterPropertiesSet(); - - assertThatCode(() -> handlerMapping.getHandlerInternal(request)).doesNotThrowAnyException(); + assertThatCode(() -> handlerMapping.get().getHandlerInternal(request)).doesNotThrowAnyException(); } private static Class createProxy(Object source) { diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/support/DelegatingHandlerMappingUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/DelegatingHandlerMappingUnitTests.java similarity index 98% rename from spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/support/DelegatingHandlerMappingUnitTests.java rename to spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/DelegatingHandlerMappingUnitTests.java index 10850edf9..a7afbf481 100755 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/support/DelegatingHandlerMappingUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/DelegatingHandlerMappingUnitTests.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.rest.webmvc.support; +package org.springframework.data.rest.webmvc.config; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail;