From b844b95a27d2953edcc5dcebed5875114e84f03a Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Tue, 29 Apr 2014 15:40:08 +0200 Subject: [PATCH] DATAREST-276 - RepositoryRestHandlerMapping now works with absolute base URIs. Refined the algorithm to detect the lookup path within the configured base URI, even if the base URI was defined in an absolute way. Also, solidified the implementation against URIs with trailing slashes. --- .../webmvc/RepositoryRestHandlerMapping.java | 75 +++++++++++++++---- ...RepositoryRestHandlerMappingUnitTests.java | 39 ++++++++++ 2 files changed, 100 insertions(+), 14 deletions(-) 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 8cd535b69..325a6bfa2 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 @@ -17,7 +17,9 @@ package org.springframework.data.rest.webmvc; import static org.springframework.util.StringUtils.*; +import java.net.URI; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -34,7 +36,6 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; -import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; /** @@ -118,29 +119,66 @@ public class RepositoryRestHandlerMapping extends RequestMappingHandlerMapping { return super.lookupHandlerMethod(lookupPath, request); } - // Strip base URI - UriComponents components = UriComponentsBuilder.fromPath(lookupPath).build(); - List segments = components.getPathSegments(); - String baseUri = config.getBaseUri().toString(); - int repositoryIndex = !segments.isEmpty() && segments.get(0).equals(baseUri) ? 1 : 0; - segments = segments.subList(repositoryIndex, segments.size()); - - String uri = "/".concat(StringUtils.collectionToDelimitedString(segments, "/")); - - request = new DefaultAcceptTypeHttpServletRequest(origRequest, acceptType, uri); + String uri = extractRepositoryLookupPath(lookupPath, config.getBaseUri()); + request = new DefaultAcceptTypeHttpServletRequest(request, acceptType, uri); // Root request - if (uri.equals("/")) { - return super.lookupHandlerMethod(uri, request); + if (!StringUtils.hasText(uri) || uri.equals("/")) { + return super.lookupHandlerMethod("/", request); } - if (mappings.exportsTopLevelResourceFor(segments.get(0))) { + String[] parts = uri.split("/"); + + if (mappings.exportsTopLevelResourceFor(parts[1])) { return super.lookupHandlerMethod(uri, request); } return null; } + /** + * Extracts the actual lookup path within the Spring Data REST managed URI space. This includes stripping the + * necessary parts of the base URI from the source lookup path. + * + * @param lookupPath must not be {@literal null}. + * @param baseUri must not be {@literal null}. + * @return + */ + private static String extractRepositoryLookupPath(String lookupPath, URI baseUri) { + + Assert.notNull(lookupPath, "Lookup path must not be null!"); + Assert.notNull(baseUri, "Base URI must not be null!"); + + lookupPath = StringUtils.trimTrailingCharacter(lookupPath, '/'); + + if (!baseUri.isAbsolute()) { + + String uri = baseUri.toString(); + + if (!StringUtils.hasText(uri)) { + return lookupPath; + } + + uri = uri.startsWith("/") ? uri : "/".concat(uri); + return lookupPath.substring(uri.length(), lookupPath.length()); + } + + List baseUriSegments = UriComponentsBuilder.fromUri(baseUri).build().getPathSegments(); + Collections.reverse(baseUriSegments); + String tail = ""; + + for (String tailSegment : baseUriSegments) { + + tail = "/".concat(tailSegment).concat(tail); + + if (lookupPath.startsWith(tail)) { + return lookupPath.substring(tail.length(), lookupPath.length()); + } + } + + return lookupPath; + } + /* * (non-Javadoc) * @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#isHandler(java.lang.Class) @@ -196,5 +234,14 @@ public class RepositoryRestHandlerMapping extends RequestMappingHandlerMapping { public String getRequestURI() { return requestUri != null ? requestUri : super.getRequestURI(); } + + /* + * (non-Javadoc) + * @see javax.servlet.http.HttpServletRequestWrapper#getServletPath() + */ + @Override + public String getServletPath() { + return requestUri != null ? requestUri : super.getServletPath(); + } } } 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 5a55fbd8a..d22e0bf72 100644 --- 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 @@ -136,4 +136,43 @@ public class RepositoryRestHandlerMappingUnitTests { assertThat(method, is(notNullValue())); assertThat(method.getMethod(), is(rootHandlerMethod)); } + + /** + * @see DATAREST-276 + */ + @Test + public void returnsRepositoryHandlerMethodForAbsoluteBaseUri() throws Exception { + + String baseUri = "http://localhost/base"; + + when(mappings.exportsTopLevelResourceFor("people")).thenReturn(true); + mockRequest = new MockHttpServletRequest("GET", baseUri.concat("/people/")); + + configuration.setBaseUri(URI.create(baseUri)); + + HandlerMethod method = handlerMapping.lookupHandlerMethod("/base/people/", mockRequest); + + assertThat(method, is(notNullValue())); + assertThat(method.getMethod(), is(listEntitiesMethod)); + } + + /** + * @see DATAREST-276 + */ + @Test + public void returnsRepositoryHandlerMethodForAbsoluteBaseUriWithServletMapping() throws Exception { + + String baseUri = "http://localhost/base"; + + when(mappings.exportsTopLevelResourceFor("people")).thenReturn(true); + mockRequest = new MockHttpServletRequest("GET", baseUri.concat("/people/")); + mockRequest.setServletPath(baseUri.concat("/people/")); + + configuration.setBaseUri(URI.create(baseUri)); + + HandlerMethod method = handlerMapping.lookupHandlerMethod("/people/", mockRequest); + + assertThat(method, is(notNullValue())); + assertThat(method.getMethod(), is(listEntitiesMethod)); + } }